1use super::JsValue;
6use super::coerce::{to_number, to_string};
7
8pub fn call(this: &[JsValue], method: &str, args: &[JsValue]) -> Option<JsValue> {
10 match method {
11 "join" => join(this, args),
12 "indexOf" => index_of(this, args),
13 "lastIndexOf" => last_index_of(this, args),
14 "includes" => includes(this, args),
15 "at" => at(this, args),
16 "toString" => join(this, &[]),
17 "concat" => concat(this, args),
18 "reverse" => reverse(this),
19 "flat" => flat(this),
20 _ => None,
21 }
22}
23
24pub fn property(this: &[JsValue], prop: &str) -> Option<JsValue> {
26 if prop == "length" {
27 return Some(JsValue::Number(this.len() as f64));
28 }
29 if let Ok(idx) = prop.parse::<usize>() {
30 return this.get(idx).cloned();
31 }
32 None
33}
34
35fn join(this: &[JsValue], args: &[JsValue]) -> Option<JsValue> {
36 let sep = args.first().map(to_string).unwrap_or_else(|| ",".to_string());
37 let parts: Vec<String> = this.iter().map(|v| match v {
38 JsValue::Null | JsValue::Undefined => String::new(),
39 other => to_string(other),
40 }).collect();
41 Some(JsValue::String(parts.join(&sep)))
42}
43
44fn index_of(this: &[JsValue], args: &[JsValue]) -> Option<JsValue> {
45 let search = args.first()?;
46 let from = args.get(1).map(|v| to_number(v) as i32).unwrap_or(0).max(0) as usize;
47 for (i, item) in this.iter().enumerate().skip(from) {
48 if super::ops::strict_eq_bool(item, search) {
49 return Some(JsValue::Number(i as f64));
50 }
51 }
52 Some(JsValue::Number(-1.0))
53}
54
55fn last_index_of(this: &[JsValue], args: &[JsValue]) -> Option<JsValue> {
56 let search = args.first()?;
57 for (i, item) in this.iter().enumerate().rev() {
58 if super::ops::strict_eq_bool(item, search) {
59 return Some(JsValue::Number(i as f64));
60 }
61 }
62 Some(JsValue::Number(-1.0))
63}
64
65fn includes(this: &[JsValue], args: &[JsValue]) -> Option<JsValue> {
66 let search = args.first()?;
67 let from = args.get(1).map(|v| to_number(v) as i32).unwrap_or(0).max(0) as usize;
68 let found = this.iter().skip(from).any(|item| super::ops::strict_eq_bool(item, search));
69 Some(JsValue::Boolean(found))
70}
71
72fn at(this: &[JsValue], args: &[JsValue]) -> Option<JsValue> {
73 let idx = args.first().map(|v| to_number(v) as i32).unwrap_or(0);
74 let len = this.len() as i32;
75 let resolved = if idx < 0 { len + idx } else { idx };
76 if resolved < 0 || resolved >= len {
77 return Some(JsValue::Undefined);
78 }
79 this.get(resolved as usize).cloned()
80}
81
82fn concat(this: &[JsValue], args: &[JsValue]) -> Option<JsValue> {
83 let _ = (this, args);
85 None
86}
87
88fn reverse(this: &[JsValue]) -> Option<JsValue> {
89 let _ = this;
90 None }
92
93fn flat(this: &[JsValue]) -> Option<JsValue> {
94 let _ = this;
95 None }
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 fn n(v: f64) -> JsValue { JsValue::Number(v) }
102 fn s(v: &str) -> JsValue { JsValue::String(v.into()) }
103
104 #[test]
105 fn test_join() {
106 let arr = [s("a"), s("b"), s("c")];
107 assert_eq!(call(&arr, "join", &[s(",")]), Some(s("a,b,c")));
108 assert_eq!(call(&arr, "join", &[s("")]), Some(s("abc")));
109 assert_eq!(call(&arr, "join", &[]), Some(s("a,b,c")));
110 }
111
112 #[test]
113 fn test_index_of() {
114 let arr = [n(1.0), n(2.0), n(3.0), n(2.0)];
115 assert_eq!(call(&arr, "indexOf", &[n(2.0)]), Some(n(1.0)));
116 assert_eq!(call(&arr, "indexOf", &[n(5.0)]), Some(n(-1.0)));
117 }
118
119 #[test]
120 fn test_includes() {
121 let arr = [n(1.0), n(2.0), n(3.0)];
122 assert_eq!(call(&arr, "includes", &[n(2.0)]), Some(JsValue::Boolean(true)));
123 assert_eq!(call(&arr, "includes", &[n(5.0)]), Some(JsValue::Boolean(false)));
124 }
125
126 #[test]
127 fn test_at() {
128 let arr = [s("a"), s("b"), s("c")];
129 assert_eq!(call(&arr, "at", &[n(0.0)]), Some(s("a")));
130 assert_eq!(call(&arr, "at", &[n(-1.0)]), Some(s("c")));
131 assert_eq!(call(&arr, "at", &[n(5.0)]), Some(JsValue::Undefined));
132 }
133
134 #[test]
135 fn test_property_length() {
136 let arr = [n(1.0), n(2.0), n(3.0)];
137 assert_eq!(property(&arr, "length"), Some(n(3.0)));
138 }
139
140 #[test]
141 fn test_bracket_access() {
142 let arr = [s("x"), s("y")];
143 assert_eq!(property(&arr, "0"), Some(s("x")));
144 assert_eq!(property(&arr, "1"), Some(s("y")));
145 }
146
147 #[test]
148 fn test_join_with_null() {
149 let arr = [s("a"), JsValue::Null, s("c")];
150 assert_eq!(call(&arr, "join", &[s(",")]), Some(s("a,,c")));
151 }
152
153 #[test]
154 fn test_to_string() {
155 let arr = [n(1.0), n(2.0), n(3.0)];
156 assert_eq!(call(&arr, "toString", &[]), Some(s("1,2,3")));
157 }
158}