1#![cfg(not(feature = "no_index"))]
2#![allow(non_snake_case)]
3
4use crate::engine::OP_EQUALS;
5use crate::plugin::*;
6use crate::{
7 def_package, Array, Dynamic, EvalAltResult, ExclusiveRange, FnPtr, InclusiveRange,
8 NativeCallContext, Position, INT,
9};
10#[cfg(feature = "no_std")]
11use std::prelude::v1::*;
12use std::{any::TypeId, cmp::Ordering, mem};
13
14def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
15 lib.standard = true;
16
17 combine_with_exported_module!(lib, "array", array_functions);
18
19 lib.set_iterable::<Array>();
21});
22
23#[export_module]
24mod array_functions {
25 #[rhai_fn(name = "len", get = "len", pure)]
26 pub fn len(array: &mut Array) -> INT {
27 array.len() as INT
28 }
29 #[rhai_fn(name = "push", name = "+=")]
30 pub fn push(array: &mut Array, item: Dynamic) {
31 array.push(item);
32 }
33 #[rhai_fn(name = "append", name = "+=")]
34 pub fn append(array: &mut Array, y: Array) {
35 if !y.is_empty() {
36 if array.is_empty() {
37 *array = y;
38 } else {
39 array.extend(y);
40 }
41 }
42 }
43 #[rhai_fn(name = "+")]
44 pub fn concat(mut array: Array, y: Array) -> Array {
45 if !y.is_empty() {
46 if array.is_empty() {
47 array = y;
48 } else {
49 array.extend(y);
50 }
51 }
52 array
53 }
54 pub fn insert(array: &mut Array, position: INT, item: Dynamic) {
55 if array.is_empty() {
56 array.push(item);
57 } else if position < 0 {
58 if let Some(n) = position.checked_abs() {
59 if n as usize > array.len() {
60 array.insert(0, item);
61 } else {
62 array.insert(array.len() - n as usize, item);
63 }
64 } else {
65 array.insert(0, item);
66 }
67 } else if (position as usize) >= array.len() {
68 array.push(item);
69 } else {
70 array.insert(position as usize, item);
71 }
72 }
73 #[rhai_fn(return_raw)]
74 pub fn pad(
75 ctx: NativeCallContext,
76 array: &mut Array,
77 len: INT,
78 item: Dynamic,
79 ) -> Result<(), Box<EvalAltResult>> {
80 if len <= 0 {
81 return Ok(());
82 }
83
84 let _ctx = ctx;
85
86 #[cfg(not(feature = "unchecked"))]
88 if _ctx.engine().max_array_size() > 0 && (len as usize) > _ctx.engine().max_array_size() {
89 return Err(EvalAltResult::ErrorDataTooLarge(
90 "Size of array".to_string(),
91 Position::NONE,
92 )
93 .into());
94 }
95
96 if len as usize > array.len() {
97 array.resize(len as usize, item);
98 }
99
100 Ok(())
101 }
102 pub fn pop(array: &mut Array) -> Dynamic {
103 if array.is_empty() {
104 Dynamic::UNIT
105 } else {
106 array.pop().unwrap_or_else(|| Dynamic::UNIT)
107 }
108 }
109 pub fn shift(array: &mut Array) -> Dynamic {
110 if array.is_empty() {
111 Dynamic::UNIT
112 } else {
113 array.remove(0)
114 }
115 }
116 pub fn remove(array: &mut Array, len: INT) -> Dynamic {
117 if len < 0 || (len as usize) >= array.len() {
118 Dynamic::UNIT
119 } else {
120 array.remove(len as usize)
121 }
122 }
123 pub fn clear(array: &mut Array) {
124 if !array.is_empty() {
125 array.clear();
126 }
127 }
128 pub fn truncate(array: &mut Array, len: INT) {
129 if !array.is_empty() {
130 if len >= 0 {
131 array.truncate(len as usize);
132 } else {
133 array.clear();
134 }
135 }
136 }
137 pub fn chop(array: &mut Array, len: INT) {
138 if !array.is_empty() && len as usize >= array.len() {
139 if len >= 0 {
140 array.drain(0..array.len() - len as usize);
141 } else {
142 array.clear();
143 }
144 }
145 }
146 pub fn reverse(array: &mut Array) {
147 if !array.is_empty() {
148 array.reverse();
149 }
150 }
151 #[rhai_fn(name = "splice")]
152 pub fn splice_range(array: &mut Array, range: ExclusiveRange, replace: Array) {
153 let start = INT::max(range.start, 0);
154 let end = INT::max(range.end, start);
155 splice(array, start, end - start, replace)
156 }
157 #[rhai_fn(name = "splice")]
158 pub fn splice_inclusive_range(array: &mut Array, range: InclusiveRange, replace: Array) {
159 let start = INT::max(*range.start(), 0);
160 let end = INT::max(*range.end(), start);
161 splice(array, start, end - start + 1, replace)
162 }
163 pub fn splice(array: &mut Array, start: INT, len: INT, replace: Array) {
164 if array.is_empty() {
165 *array = replace;
166 return;
167 }
168
169 let start = if start < 0 {
170 let arr_len = array.len();
171 start
172 .checked_abs()
173 .map_or(0, |n| arr_len - (n as usize).min(arr_len))
174 } else if start as usize >= array.len() {
175 array.extend(replace.into_iter());
176 return;
177 } else {
178 start as usize
179 };
180
181 let len = if len < 0 {
182 0
183 } else if len as usize > array.len() - start {
184 array.len() - start
185 } else {
186 len as usize
187 };
188
189 array.splice(start..start + len, replace.into_iter());
190 }
191 #[rhai_fn(name = "extract")]
192 pub fn extract_range(array: &mut Array, range: ExclusiveRange) -> Array {
193 let start = INT::max(range.start, 0);
194 let end = INT::max(range.end, start);
195 extract(array, start, end - start)
196 }
197 #[rhai_fn(name = "extract")]
198 pub fn extract_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
199 let start = INT::max(*range.start(), 0);
200 let end = INT::max(*range.end(), start);
201 extract(array, start, end - start + 1)
202 }
203 pub fn extract(array: &mut Array, start: INT, len: INT) -> Array {
204 if array.is_empty() || len <= 0 {
205 return Array::new();
206 }
207
208 let start = if start < 0 {
209 let arr_len = array.len();
210 start
211 .checked_abs()
212 .map_or(0, |n| arr_len - (n as usize).min(arr_len))
213 } else if start as usize >= array.len() {
214 return Array::new();
215 } else {
216 start as usize
217 };
218
219 let len = if len <= 0 {
220 0
221 } else if len as usize > array.len() - start {
222 array.len() - start
223 } else {
224 len as usize
225 };
226
227 if len == 0 {
228 Array::new()
229 } else {
230 array[start..start + len].to_vec()
231 }
232 }
233 #[rhai_fn(name = "extract")]
234 pub fn extract_tail(array: &mut Array, start: INT) -> Array {
235 extract(array, start, INT::MAX)
236 }
237 #[rhai_fn(name = "split")]
238 pub fn split_at(array: &mut Array, start: INT) -> Array {
239 if array.is_empty() {
240 Array::new()
241 } else if start < 0 {
242 if let Some(n) = start.checked_abs() {
243 if n as usize > array.len() {
244 mem::take(array)
245 } else {
246 let mut result = Array::new();
247 result.extend(array.drain(array.len() - n as usize..));
248 result
249 }
250 } else {
251 mem::take(array)
252 }
253 } else if start as usize >= array.len() {
254 Array::new()
255 } else {
256 let mut result = Array::new();
257 result.extend(array.drain(start as usize..));
258 result
259 }
260 }
261 #[rhai_fn(return_raw, pure)]
262 pub fn map(
263 ctx: NativeCallContext,
264 array: &mut Array,
265 mapper: FnPtr,
266 ) -> Result<Array, Box<EvalAltResult>> {
267 if array.is_empty() {
268 return Ok(array.clone());
269 }
270
271 let mut ar = Array::with_capacity(array.len());
272
273 for (i, item) in array.iter().enumerate() {
274 ar.push(
275 mapper
276 .call_raw(&ctx, None, [item.clone()])
277 .or_else(|err| match *err {
278 EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
279 if fn_sig.starts_with(mapper.fn_name()) =>
280 {
281 mapper.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
282 }
283 _ => Err(err),
284 })
285 .map_err(|err| {
286 Box::new(EvalAltResult::ErrorInFunctionCall(
287 "map".to_string(),
288 ctx.source().unwrap_or("").to_string(),
289 err,
290 Position::NONE,
291 ))
292 })?,
293 );
294 }
295
296 Ok(ar)
297 }
298 #[rhai_fn(name = "map", return_raw, pure)]
299 pub fn map_with_fn_name(
300 ctx: NativeCallContext,
301 array: &mut Array,
302 mapper: &str,
303 ) -> Result<Array, Box<EvalAltResult>> {
304 map(ctx, array, FnPtr::new(mapper)?)
305 }
306
307 #[rhai_fn(return_raw, pure)]
308 pub fn filter(
309 ctx: NativeCallContext,
310 array: &mut Array,
311 filter: FnPtr,
312 ) -> Result<Array, Box<EvalAltResult>> {
313 if array.is_empty() {
314 return Ok(array.clone());
315 }
316
317 let mut ar = Array::new();
318
319 for (i, item) in array.iter().enumerate() {
320 if filter
321 .call_raw(&ctx, None, [item.clone()])
322 .or_else(|err| match *err {
323 EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
324 if fn_sig.starts_with(filter.fn_name()) =>
325 {
326 filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
327 }
328 _ => Err(err),
329 })
330 .map_err(|err| {
331 Box::new(EvalAltResult::ErrorInFunctionCall(
332 "filter".to_string(),
333 ctx.source().unwrap_or("").to_string(),
334 err,
335 Position::NONE,
336 ))
337 })?
338 .as_bool()
339 .unwrap_or(false)
340 {
341 ar.push(item.clone());
342 }
343 }
344
345 Ok(ar)
346 }
347 #[rhai_fn(name = "filter", return_raw, pure)]
348 pub fn filter_with_fn_name(
349 ctx: NativeCallContext,
350 array: &mut Array,
351 filter_func: &str,
352 ) -> Result<Array, Box<EvalAltResult>> {
353 filter(ctx, array, FnPtr::new(filter_func)?)
354 }
355 #[rhai_fn(return_raw, pure)]
356 pub fn contains(
357 ctx: NativeCallContext,
358 array: &mut Array,
359 value: Dynamic,
360 ) -> Result<bool, Box<EvalAltResult>> {
361 if array.is_empty() {
362 return Ok(false);
363 }
364
365 for item in array.iter_mut() {
366 if ctx
367 .call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
368 .or_else(|err| match *err {
369 EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _)
370 if fn_sig.starts_with(OP_EQUALS) =>
371 {
372 if item.type_id() == value.type_id() {
373 Err(err)
375 } else {
376 Ok(Dynamic::FALSE)
377 }
378 }
379 _ => Err(err),
380 })?
381 .as_bool()
382 .unwrap_or(false)
383 {
384 return Ok(true);
385 }
386 }
387
388 Ok(false)
389 }
390 #[rhai_fn(return_raw, pure)]
391 pub fn index_of(
392 ctx: NativeCallContext,
393 array: &mut Array,
394 value: Dynamic,
395 ) -> Result<INT, Box<EvalAltResult>> {
396 if array.is_empty() {
397 Ok(-1)
398 } else {
399 index_of_starting_from(ctx, array, value, 0)
400 }
401 }
402 #[rhai_fn(name = "index_of", return_raw, pure)]
403 pub fn index_of_starting_from(
404 ctx: NativeCallContext,
405 array: &mut Array,
406 value: Dynamic,
407 start: INT,
408 ) -> Result<INT, Box<EvalAltResult>> {
409 if array.is_empty() {
410 return Ok(-1);
411 }
412
413 let start = if start < 0 {
414 let arr_len = array.len();
415 start
416 .checked_abs()
417 .map_or(0, |n| arr_len - (n as usize).min(arr_len))
418 } else if start as usize >= array.len() {
419 return Ok(-1);
420 } else {
421 start as usize
422 };
423
424 for (i, item) in array.iter_mut().enumerate().skip(start) {
425 if ctx
426 .call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
427 .or_else(|err| match *err {
428 EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _)
429 if fn_sig.starts_with(OP_EQUALS) =>
430 {
431 if item.type_id() == value.type_id() {
432 Err(err)
434 } else {
435 Ok(Dynamic::FALSE)
436 }
437 }
438 _ => Err(err),
439 })?
440 .as_bool()
441 .unwrap_or(false)
442 {
443 return Ok(i as INT);
444 }
445 }
446
447 Ok(-1 as INT)
448 }
449 #[rhai_fn(name = "index_of", return_raw, pure)]
450 pub fn index_of_with_fn_name(
451 ctx: NativeCallContext,
452 array: &mut Array,
453 filter: &str,
454 ) -> Result<INT, Box<EvalAltResult>> {
455 index_of_filter(ctx, array, FnPtr::new(filter)?)
456 }
457 #[rhai_fn(name = "index_of", return_raw, pure)]
458 pub fn index_of_filter(
459 ctx: NativeCallContext,
460 array: &mut Array,
461 filter: FnPtr,
462 ) -> Result<INT, Box<EvalAltResult>> {
463 if array.is_empty() {
464 Ok(-1)
465 } else {
466 index_of_filter_starting_from(ctx, array, filter, 0)
467 }
468 }
469 #[rhai_fn(name = "index_of", return_raw, pure)]
470 pub fn index_of_filter_starting_from(
471 ctx: NativeCallContext,
472 array: &mut Array,
473 filter: FnPtr,
474 start: INT,
475 ) -> Result<INT, Box<EvalAltResult>> {
476 if array.is_empty() {
477 return Ok(-1);
478 }
479
480 let start = if start < 0 {
481 let arr_len = array.len();
482 start
483 .checked_abs()
484 .map_or(0, |n| arr_len - (n as usize).min(arr_len))
485 } else if start as usize >= array.len() {
486 return Ok(-1);
487 } else {
488 start as usize
489 };
490
491 for (i, item) in array.iter().enumerate().skip(start) {
492 if filter
493 .call_raw(&ctx, None, [item.clone()])
494 .or_else(|err| match *err {
495 EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
496 if fn_sig.starts_with(filter.fn_name()) =>
497 {
498 filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
499 }
500 _ => Err(err),
501 })
502 .map_err(|err| {
503 Box::new(EvalAltResult::ErrorInFunctionCall(
504 "index_of".to_string(),
505 ctx.source().unwrap_or("").to_string(),
506 err,
507 Position::NONE,
508 ))
509 })?
510 .as_bool()
511 .unwrap_or(false)
512 {
513 return Ok(i as INT);
514 }
515 }
516
517 Ok(-1 as INT)
518 }
519 #[rhai_fn(name = "index_of", return_raw, pure)]
520 pub fn index_of_with_fn_name_filter_starting_from(
521 ctx: NativeCallContext,
522 array: &mut Array,
523 filter: &str,
524 start: INT,
525 ) -> Result<INT, Box<EvalAltResult>> {
526 index_of_filter_starting_from(ctx, array, FnPtr::new(filter)?, start)
527 }
528 #[rhai_fn(return_raw, pure)]
529 pub fn some(
530 ctx: NativeCallContext,
531 array: &mut Array,
532 filter: FnPtr,
533 ) -> Result<bool, Box<EvalAltResult>> {
534 if array.is_empty() {
535 return Ok(false);
536 }
537
538 for (i, item) in array.iter().enumerate() {
539 if filter
540 .call_raw(&ctx, None, [item.clone()])
541 .or_else(|err| match *err {
542 EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
543 if fn_sig.starts_with(filter.fn_name()) =>
544 {
545 filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
546 }
547 _ => Err(err),
548 })
549 .map_err(|err| {
550 Box::new(EvalAltResult::ErrorInFunctionCall(
551 "some".to_string(),
552 ctx.source().unwrap_or("").to_string(),
553 err,
554 Position::NONE,
555 ))
556 })?
557 .as_bool()
558 .unwrap_or(false)
559 {
560 return Ok(true);
561 }
562 }
563
564 Ok(false)
565 }
566 #[rhai_fn(name = "some", return_raw, pure)]
567 pub fn some_with_fn_name(
568 ctx: NativeCallContext,
569 array: &mut Array,
570 filter: &str,
571 ) -> Result<bool, Box<EvalAltResult>> {
572 some(ctx, array, FnPtr::new(filter)?)
573 }
574 #[rhai_fn(return_raw, pure)]
575 pub fn all(
576 ctx: NativeCallContext,
577 array: &mut Array,
578 filter: FnPtr,
579 ) -> Result<bool, Box<EvalAltResult>> {
580 if array.is_empty() {
581 return Ok(true);
582 }
583
584 for (i, item) in array.iter().enumerate() {
585 if !filter
586 .call_raw(&ctx, None, [item.clone()])
587 .or_else(|err| match *err {
588 EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
589 if fn_sig.starts_with(filter.fn_name()) =>
590 {
591 filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
592 }
593 _ => Err(err),
594 })
595 .map_err(|err| {
596 Box::new(EvalAltResult::ErrorInFunctionCall(
597 "all".to_string(),
598 ctx.source().unwrap_or("").to_string(),
599 err,
600 Position::NONE,
601 ))
602 })?
603 .as_bool()
604 .unwrap_or(false)
605 {
606 return Ok(false);
607 }
608 }
609
610 Ok(true)
611 }
612 #[rhai_fn(name = "all", return_raw, pure)]
613 pub fn all_with_fn_name(
614 ctx: NativeCallContext,
615 array: &mut Array,
616 filter: &str,
617 ) -> Result<bool, Box<EvalAltResult>> {
618 all(ctx, array, FnPtr::new(filter)?)
619 }
620 #[rhai_fn(return_raw)]
621 pub fn dedup(ctx: NativeCallContext, array: &mut Array) -> Result<(), Box<EvalAltResult>> {
622 dedup_with_fn_name(ctx, array, OP_EQUALS)
623 }
624 #[rhai_fn(name = "dedup", return_raw)]
625 pub fn dedup_by_comparer(
626 ctx: NativeCallContext,
627 array: &mut Array,
628 comparer: FnPtr,
629 ) -> Result<(), Box<EvalAltResult>> {
630 if array.is_empty() {
631 return Ok(());
632 }
633
634 array.dedup_by(|x, y| {
635 comparer
636 .call_raw(&ctx, None, [x.clone(), y.clone()])
637 .unwrap_or_else(|_| Dynamic::FALSE)
638 .as_bool()
639 .unwrap_or(false)
640 });
641
642 Ok(())
643 }
644 #[rhai_fn(name = "dedup", return_raw)]
645 fn dedup_with_fn_name(
646 ctx: NativeCallContext,
647 array: &mut Array,
648 comparer: &str,
649 ) -> Result<(), Box<EvalAltResult>> {
650 dedup_by_comparer(ctx, array, FnPtr::new(comparer)?)
651 }
652 #[rhai_fn(return_raw, pure)]
653 pub fn reduce(
654 ctx: NativeCallContext,
655 array: &mut Array,
656 reducer: FnPtr,
657 ) -> Result<Dynamic, Box<EvalAltResult>> {
658 reduce_with_initial(ctx, array, reducer, Dynamic::UNIT)
659 }
660 #[rhai_fn(name = "reduce", return_raw, pure)]
661 pub fn reduce_with_fn_name(
662 ctx: NativeCallContext,
663 array: &mut Array,
664 reducer: &str,
665 ) -> Result<Dynamic, Box<EvalAltResult>> {
666 reduce(ctx, array, FnPtr::new(reducer)?)
667 }
668 #[rhai_fn(name = "reduce", return_raw, pure)]
669 pub fn reduce_with_initial(
670 ctx: NativeCallContext,
671 array: &mut Array,
672 reducer: FnPtr,
673 initial: Dynamic,
674 ) -> Result<Dynamic, Box<EvalAltResult>> {
675 if array.is_empty() {
676 return Ok(initial);
677 }
678
679 let mut result = initial;
680
681 for (i, item) in array.iter().enumerate() {
682 let item = item.clone();
683
684 result = reducer
685 .call_raw(&ctx, None, [result.clone(), item.clone()])
686 .or_else(|err| match *err {
687 EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
688 if fn_sig.starts_with(reducer.fn_name()) =>
689 {
690 reducer.call_raw(&ctx, None, [result, item, (i as INT).into()])
691 }
692 _ => Err(err),
693 })
694 .map_err(|err| {
695 Box::new(EvalAltResult::ErrorInFunctionCall(
696 "reduce".to_string(),
697 ctx.source().unwrap_or("").to_string(),
698 err,
699 Position::NONE,
700 ))
701 })?;
702 }
703
704 Ok(result)
705 }
706 #[rhai_fn(name = "reduce", return_raw, pure)]
707 pub fn reduce_with_fn_name_with_initial(
708 ctx: NativeCallContext,
709 array: &mut Array,
710 reducer: &str,
711 initial: Dynamic,
712 ) -> Result<Dynamic, Box<EvalAltResult>> {
713 reduce_with_initial(ctx, array, FnPtr::new(reducer)?, initial)
714 }
715 #[rhai_fn(return_raw, pure)]
716 pub fn reduce_rev(
717 ctx: NativeCallContext,
718 array: &mut Array,
719 reducer: FnPtr,
720 ) -> Result<Dynamic, Box<EvalAltResult>> {
721 reduce_rev_with_initial(ctx, array, reducer, Dynamic::UNIT)
722 }
723 #[rhai_fn(name = "reduce_rev", return_raw, pure)]
724 pub fn reduce_rev_with_fn_name(
725 ctx: NativeCallContext,
726 array: &mut Array,
727 reducer: &str,
728 ) -> Result<Dynamic, Box<EvalAltResult>> {
729 reduce_rev(ctx, array, FnPtr::new(reducer)?)
730 }
731 #[rhai_fn(name = "reduce_rev", return_raw, pure)]
732 pub fn reduce_rev_with_initial(
733 ctx: NativeCallContext,
734 array: &mut Array,
735 reducer: FnPtr,
736 initial: Dynamic,
737 ) -> Result<Dynamic, Box<EvalAltResult>> {
738 if array.is_empty() {
739 return Ok(initial);
740 }
741
742 let mut result = initial;
743 let len = array.len();
744
745 for (i, item) in array.iter().rev().enumerate() {
746 let item = item.clone();
747
748 result = reducer
749 .call_raw(&ctx, None, [result.clone(), item.clone()])
750 .or_else(|err| match *err {
751 EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
752 if fn_sig.starts_with(reducer.fn_name()) =>
753 {
754 reducer.call_raw(&ctx, None, [result, item, ((len - 1 - i) as INT).into()])
755 }
756 _ => Err(err),
757 })
758 .map_err(|err| {
759 Box::new(EvalAltResult::ErrorInFunctionCall(
760 "reduce_rev".to_string(),
761 ctx.source().unwrap_or("").to_string(),
762 err,
763 Position::NONE,
764 ))
765 })?;
766 }
767
768 Ok(result)
769 }
770 #[rhai_fn(name = "reduce_rev", return_raw, pure)]
771 pub fn reduce_rev_with_fn_name_with_initial(
772 ctx: NativeCallContext,
773 array: &mut Array,
774 reducer: &str,
775 initial: Dynamic,
776 ) -> Result<Dynamic, Box<EvalAltResult>> {
777 reduce_rev_with_initial(ctx, array, FnPtr::new(reducer)?, initial)
778 }
779 #[rhai_fn(name = "sort", return_raw)]
780 pub fn sort_with_fn_name(
781 ctx: NativeCallContext,
782 array: &mut Array,
783 comparer: &str,
784 ) -> Result<(), Box<EvalAltResult>> {
785 sort(ctx, array, FnPtr::new(comparer)?)
786 }
787 #[rhai_fn(return_raw)]
788 pub fn sort(
789 ctx: NativeCallContext,
790 array: &mut Array,
791 comparer: FnPtr,
792 ) -> Result<(), Box<EvalAltResult>> {
793 if array.len() <= 1 {
794 return Ok(());
795 }
796
797 array.sort_by(|x, y| {
798 comparer
799 .call_raw(&ctx, None, [x.clone(), y.clone()])
800 .ok()
801 .and_then(|v| v.as_int().ok())
802 .map(|v| match v {
803 v if v > 0 => Ordering::Greater,
804 v if v < 0 => Ordering::Less,
805 0 => Ordering::Equal,
806 _ => unreachable!(),
807 })
808 .unwrap_or_else(|| x.type_id().cmp(&y.type_id()))
809 });
810
811 Ok(())
812 }
813 #[rhai_fn(name = "sort", return_raw)]
814 pub fn sort_with_builtin(array: &mut Array) -> Result<(), Box<EvalAltResult>> {
815 if array.len() <= 1 {
816 return Ok(());
817 }
818
819 let type_id = array[0].type_id();
820
821 if array.iter().any(|a| a.type_id() != type_id) {
822 return Err(EvalAltResult::ErrorFunctionNotFound(
823 "sort() cannot be called with elements of different types".into(),
824 Position::NONE,
825 )
826 .into());
827 }
828
829 if type_id == TypeId::of::<INT>() {
830 array.sort_by(|a, b| {
831 let a = a.as_int().expect("`INT`");
832 let b = b.as_int().expect("`INT`");
833 a.cmp(&b)
834 });
835 return Ok(());
836 }
837 if type_id == TypeId::of::<char>() {
838 array.sort_by(|a, b| {
839 let a = a.as_char().expect("char");
840 let b = b.as_char().expect("char");
841 a.cmp(&b)
842 });
843 return Ok(());
844 }
845 #[cfg(not(feature = "no_float"))]
846 if type_id == TypeId::of::<crate::FLOAT>() {
847 array.sort_by(|a, b| {
848 let a = a.as_float().expect("`FLOAT`");
849 let b = b.as_float().expect("`FLOAT`");
850 a.partial_cmp(&b).unwrap_or(Ordering::Equal)
851 });
852 return Ok(());
853 }
854 if type_id == TypeId::of::<ImmutableString>() {
855 array.sort_by(|a, b| {
856 let a = a.read_lock::<ImmutableString>().expect("`ImmutableString`");
857 let b = b.read_lock::<ImmutableString>().expect("`ImmutableString`");
858 a.as_str().cmp(b.as_str())
859 });
860 return Ok(());
861 }
862 #[cfg(feature = "decimal")]
863 if type_id == TypeId::of::<rust_decimal::Decimal>() {
864 array.sort_by(|a, b| {
865 let a = a.as_decimal().expect("`Decimal`");
866 let b = b.as_decimal().expect("`Decimal`");
867 a.cmp(&b)
868 });
869 return Ok(());
870 }
871 if type_id == TypeId::of::<bool>() {
872 array.sort_by(|a, b| {
873 let a = a.as_bool().expect("`bool`");
874 let b = b.as_bool().expect("`bool`");
875 a.cmp(&b)
876 });
877 return Ok(());
878 }
879 if type_id == TypeId::of::<()>() {
880 return Ok(());
881 }
882
883 Ok(())
884 }
885 #[rhai_fn(return_raw)]
886 pub fn drain(
887 ctx: NativeCallContext,
888 array: &mut Array,
889 filter: FnPtr,
890 ) -> Result<Array, Box<EvalAltResult>> {
891 if array.is_empty() {
892 return Ok(Array::new());
893 }
894
895 let mut drained = Array::with_capacity(array.len());
896
897 let mut i = 0;
898 let mut x = 0;
899
900 while x < array.len() {
901 if filter
902 .call_raw(&ctx, None, [array[x].clone()])
903 .or_else(|err| match *err {
904 EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
905 if fn_sig.starts_with(filter.fn_name()) =>
906 {
907 filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()])
908 }
909 _ => Err(err),
910 })
911 .map_err(|err| {
912 Box::new(EvalAltResult::ErrorInFunctionCall(
913 "drain".to_string(),
914 ctx.source().unwrap_or("").to_string(),
915 err,
916 Position::NONE,
917 ))
918 })?
919 .as_bool()
920 .unwrap_or(false)
921 {
922 drained.push(array.remove(x));
923 } else {
924 x += 1;
925 }
926
927 i += 1;
928 }
929
930 Ok(drained)
931 }
932 #[rhai_fn(name = "drain", return_raw)]
933 pub fn drain_with_fn_name(
934 ctx: NativeCallContext,
935 array: &mut Array,
936 filter: &str,
937 ) -> Result<Array, Box<EvalAltResult>> {
938 drain(ctx, array, FnPtr::new(filter)?)
939 }
940 #[rhai_fn(name = "drain")]
941 pub fn drain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array {
942 let start = INT::max(range.start, 0);
943 let end = INT::max(range.end, start);
944 drain_range(array, start, end - start)
945 }
946 #[rhai_fn(name = "drain")]
947 pub fn drain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
948 let start = INT::max(*range.start(), 0);
949 let end = INT::max(*range.end(), start);
950 drain_range(array, start, end - start + 1)
951 }
952 #[rhai_fn(name = "drain")]
953 pub fn drain_range(array: &mut Array, start: INT, len: INT) -> Array {
954 if array.is_empty() || len <= 0 {
955 return Array::new();
956 }
957
958 let start = if start < 0 {
959 let arr_len = array.len();
960 start
961 .checked_abs()
962 .map_or(0, |n| arr_len - (n as usize).min(arr_len))
963 } else if start as usize >= array.len() {
964 return Array::new();
965 } else {
966 start as usize
967 };
968
969 let len = if len <= 0 {
970 0
971 } else if len as usize > array.len() - start {
972 array.len() - start
973 } else {
974 len as usize
975 };
976
977 array.drain(start..start + len).collect()
978 }
979 #[rhai_fn(return_raw)]
980 pub fn retain(
981 ctx: NativeCallContext,
982 array: &mut Array,
983 filter: FnPtr,
984 ) -> Result<Array, Box<EvalAltResult>> {
985 if array.is_empty() {
986 return Ok(Array::new());
987 }
988
989 let mut drained = Array::new();
990
991 let mut i = 0;
992 let mut x = 0;
993
994 while x < array.len() {
995 if !filter
996 .call_raw(&ctx, None, [array[x].clone()])
997 .or_else(|err| match *err {
998 EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
999 if fn_sig.starts_with(filter.fn_name()) =>
1000 {
1001 filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()])
1002 }
1003 _ => Err(err),
1004 })
1005 .map_err(|err| {
1006 Box::new(EvalAltResult::ErrorInFunctionCall(
1007 "retain".to_string(),
1008 ctx.source().unwrap_or("").to_string(),
1009 err,
1010 Position::NONE,
1011 ))
1012 })?
1013 .as_bool()
1014 .unwrap_or(false)
1015 {
1016 drained.push(array.remove(x));
1017 } else {
1018 x += 1;
1019 }
1020
1021 i += 1;
1022 }
1023
1024 Ok(drained)
1025 }
1026 #[rhai_fn(name = "retain", return_raw)]
1027 pub fn retain_with_fn_name(
1028 ctx: NativeCallContext,
1029 array: &mut Array,
1030 filter: &str,
1031 ) -> Result<Array, Box<EvalAltResult>> {
1032 retain(ctx, array, FnPtr::new(filter)?)
1033 }
1034 #[rhai_fn(name = "retain")]
1035 pub fn retain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array {
1036 let start = INT::max(range.start, 0);
1037 let end = INT::max(range.end, start);
1038 retain_range(array, start, end - start)
1039 }
1040 #[rhai_fn(name = "retain")]
1041 pub fn retain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
1042 let start = INT::max(*range.start(), 0);
1043 let end = INT::max(*range.end(), start);
1044 retain_range(array, start, end - start + 1)
1045 }
1046 #[rhai_fn(name = "retain")]
1047 pub fn retain_range(array: &mut Array, start: INT, len: INT) -> Array {
1048 if array.is_empty() || len <= 0 {
1049 return Array::new();
1050 }
1051
1052 let start = if start < 0 {
1053 let arr_len = array.len();
1054 start
1055 .checked_abs()
1056 .map_or(0, |n| arr_len - (n as usize).min(arr_len))
1057 } else if start as usize >= array.len() {
1058 return mem::take(array);
1059 } else {
1060 start as usize
1061 };
1062
1063 let len = if len < 0 {
1064 0
1065 } else if len as usize > array.len() - start {
1066 array.len() - start
1067 } else {
1068 len as usize
1069 };
1070
1071 let mut drained: Array = array.drain(..start).collect();
1072 drained.extend(array.drain(len..));
1073
1074 drained
1075 }
1076 #[rhai_fn(name = "==", return_raw, pure)]
1077 pub fn equals(
1078 ctx: NativeCallContext,
1079 array1: &mut Array,
1080 array2: Array,
1081 ) -> Result<bool, Box<EvalAltResult>> {
1082 if array1.len() != array2.len() {
1083 return Ok(false);
1084 }
1085 if array1.is_empty() {
1086 return Ok(true);
1087 }
1088
1089 let mut array2 = array2;
1090
1091 for (a1, a2) in array1.iter_mut().zip(array2.iter_mut()) {
1092 if !ctx
1093 .call_fn_raw(OP_EQUALS, true, false, &mut [a1, a2])
1094 .or_else(|err| match *err {
1095 EvalAltResult::ErrorFunctionNotFound(ref fn_sig, _)
1096 if fn_sig.starts_with(OP_EQUALS) =>
1097 {
1098 if a1.type_id() == a2.type_id() {
1099 Err(err)
1101 } else {
1102 Ok(Dynamic::FALSE)
1103 }
1104 }
1105 _ => Err(err),
1106 })?
1107 .as_bool()
1108 .unwrap_or(false)
1109 {
1110 return Ok(false);
1111 }
1112 }
1113
1114 Ok(true)
1115 }
1116 #[rhai_fn(name = "!=", return_raw, pure)]
1117 pub fn not_equals(
1118 ctx: NativeCallContext,
1119 array1: &mut Array,
1120 array2: Array,
1121 ) -> Result<bool, Box<EvalAltResult>> {
1122 equals(ctx, array1, array2).map(|r| !r)
1123 }
1124}