1use indexmap::IndexMap;
2use crate::{bail, Parser, Value};
3
4pub fn add_array_functions(p: &mut Parser) {
5 p.add_function("all", |c| {
6 if c.args.len() != 1 {
7 bail!("all() takes exactly one argument and a predicate");
8 }
9 if let (Value::Array(a), Some(predicate)) = (&c.args[0], c.predicate) {
10 for value in a {
11 let mut ctx = c.ctx.clone();
12 ctx.insert("#".to_string(), value.clone());
13 if let Value::Bool(false) = c.parser.run(predicate.clone(), &ctx)? {
14 return Ok(false.into());
15 }
16 }
17 Ok(true.into())
18 } else {
19 bail!("all() takes an array as the first argument");
20 }
21 });
22
23 p.add_function("any", |c| {
24 if c.args.len() != 1 {
25 bail!("any() takes exactly one argument and a predicate");
26 }
27 if let (Value::Array(a), Some(predicate)) = (&c.args[0], c.predicate) {
28 for value in a {
29 let mut ctx = c.ctx.clone();
30 ctx.insert("#".to_string(), value.clone());
31 if let Value::Bool(true) = c.parser.run(predicate.clone(), &ctx)? {
32 return Ok(true.into());
33 }
34 }
35 Ok(false.into())
36 } else {
37 bail!("any() takes an array as the first argument");
38 }
39 });
40
41 p.add_function("one", |c| {
42 if c.args.len() != 1 {
43 bail!("one() takes exactly one argument and a predicate");
44 }
45 if let (Value::Array(a), Some(predicate)) = (&c.args[0], c.predicate) {
46 let mut found = false;
47 for value in a {
48 let mut ctx = c.ctx.clone();
49 ctx.insert("#".to_string(), value.clone());
50 if let Value::Bool(true) = c.parser.run(predicate.clone(), &ctx)? {
51 if found {
52 return Ok(false.into());
53 }
54 found = true;
55 }
56 }
57 Ok(found.into())
58 } else {
59 bail!("one() takes an array as the first argument");
60 }
61 });
62
63 p.add_function("none", |c| {
64 if c.args.len() != 1 {
65 bail!("none() takes exactly one argument and a predicate");
66 }
67 if let (Value::Array(a), Some(predicate)) = (&c.args[0], c.predicate) {
68 for value in a {
69 let mut ctx = c.ctx.clone();
70 ctx.insert("#".to_string(), value.clone());
71 if let Value::Bool(true) = c.parser.run(predicate.clone(), &ctx)? {
72 return Ok(false.into());
73 }
74 }
75 Ok(true.into())
76 } else {
77 bail!("none() takes an array as the first argument");
78 }
79 });
80
81 p.add_function("map", |c| {
82 let mut result = Vec::new();
83 if c.args.len() != 1 {
84 bail!("map() takes exactly one argument and a predicate");
85 }
86 if let (Value::Array(a), Some(predicate)) = (&c.args[0], c.predicate) {
87 for value in a {
88 let mut ctx = c.ctx.clone();
89 ctx.insert("#".to_string(), value.clone());
90 result.push(c.parser.run(predicate.clone(), &ctx)?);
91 }
92 } else {
93 bail!("map() takes an array as the first argument");
94 }
95 Ok(result.into())
96 });
97
98 p.add_function("filter", |c| {
99 let mut result = Vec::new();
100 if c.args.len() != 1 {
101 bail!("filter() takes exactly one argument and a predicate");
102 }
103 if let (Value::Array(a), Some(predicate)) = (&c.args[0], c.predicate) {
104 for value in a {
105 let mut ctx = c.ctx.clone();
106 ctx.insert("#".to_string(), value.clone());
107 if let Value::Bool(true) = c.parser.run(predicate.clone(), &ctx)? {
108 result.push(value.clone());
109 }
110 }
111 } else {
112 bail!("filter() takes an array as the first argument");
113 }
114 Ok(result.into())
115 });
116
117 p.add_function("find", |c| {
118 if c.args.len() != 1 {
119 bail!("find() takes exactly one argument and a predicate");
120 }
121 if let (Value::Array(a), Some(predicate)) = (&c.args[0], c.predicate) {
122 for value in a {
123 let mut ctx = c.ctx.clone();
124 ctx.insert("#".to_string(), value.clone());
125 if let Value::Bool(true) = c.parser.run(predicate.clone(), &ctx)? {
126 return Ok(value.clone());
127 }
128 }
129 Ok(Value::Nil)
130 } else {
131 bail!("find() takes an array as the first argument");
132 }
133 });
134
135 p.add_function("findIndex", |c| {
136 if c.args.len() != 1 {
137 bail!("findIndex() takes exactly one argument and a predicate");
138 }
139 if let (Value::Array(a), Some(predicate)) = (&c.args[0], c.predicate) {
140 for (i, value) in a.iter().enumerate() {
141 let mut ctx = c.ctx.clone();
142 ctx.insert("#".to_string(), value.clone());
143 if let Value::Bool(true) = c.parser.run(predicate.clone(), &ctx)? {
144 return Ok(i.into());
145 }
146 }
147 Ok(Value::Number(-1))
148 } else {
149 bail!("findIndex() takes an array as the first argument");
150 }
151 });
152
153 p.add_function("findLast", |c| {
154 if c.args.len() != 1 {
155 bail!("findLast() takes exactly one argument and a predicate");
156 }
157 if let (Value::Array(a), Some(predicate)) = (&c.args[0], c.predicate) {
158 for value in a.iter().rev() {
159 let mut ctx = c.ctx.clone();
160 ctx.insert("#".to_string(), value.clone());
161 if let Value::Bool(true) = c.parser.run(predicate.clone(), &ctx)? {
162 return Ok(value.clone());
163 }
164 }
165 Ok(Value::Nil)
166 } else {
167 bail!("findLast() takes an array as the first argument");
168 }
169 });
170
171 p.add_function("findLastIndex", |c| {
172 if c.args.len() != 1 {
173 bail!("findLastIndex() takes exactly one argument and a predicate");
174 }
175 if let (Value::Array(a), Some(predicate)) = (&c.args[0], c.predicate) {
176 for (i, value) in a.iter().enumerate().rev() {
177 let mut ctx = c.ctx.clone();
178 ctx.insert("#".to_string(), value.clone());
179 if let Value::Bool(true) = c.parser.run(predicate.clone(), &ctx)? {
180 return Ok(i.into());
181 }
182 }
183 Ok(Value::Number(-1))
184 } else {
185 bail!("findLastIndex() takes an array as the first argument");
186 }
187 });
188 p.add_function("groupBy", |c| {
189 if c.args.len() != 1 {
190 bail!("groupBy() takes exactly two arguments");
191 }
192 if let (Value::Array(a), Some(predicate)) = (&c.args[0], c.predicate) {
193 let mut groups = IndexMap::new();
194 for value in a {
195 let mut ctx = c.ctx.clone();
196 ctx.insert("#".to_string(), value.clone());
197 if let Some(key) = c.parser.run(predicate.clone(), &ctx)?.as_string() {
198 groups.entry(key.to_string()).or_insert_with(Vec::new).push(value.clone());
199 } else {
200 bail!("groupBy() predicate must return a string");
201 }
202 }
203 Ok(Value::Map(groups.into_iter().map(|(k, group)| (k, group.into())).collect()))
204 } else {
205 bail!("groupBy() takes an array as the first argument and a predicate as the second argument");
206 }
207 });
208}