1use nu_engine::ClosureEval;
2use nu_protocol::shell_error::generic::GenericError;
3use nu_protocol::{PipelineData, Record, ShellError, Span, Value, ast::CellPath};
4use nu_utils::IgnoreCaseExt;
5use std::cmp::Ordering;
6
7pub enum Comparator {
12 KeyClosure(ClosureEval),
13 CustomClosure(ClosureEval),
14 CellPath(CellPath),
15}
16
17pub fn sort(vec: &mut [Value], insensitive: bool, natural: bool) -> Result<(), ShellError> {
29 let mut compare_err: Option<ShellError> = None;
33
34 vec.sort_by(|a, b| {
35 if compare_err.is_some() {
37 return Ordering::Equal;
38 }
39
40 compare_values(a, b, insensitive, natural).unwrap_or_else(|err| {
41 compare_err.get_or_insert(err);
42 Ordering::Equal
43 })
44 });
45
46 if let Some(err) = compare_err {
47 Err(err)
48 } else {
49 Ok(())
50 }
51}
52
53pub fn sort_by(
55 vec: &mut [Value],
56 mut comparators: Vec<Comparator>,
57 head_span: Span,
58 insensitive: bool,
59 natural: bool,
60) -> Result<(), ShellError> {
61 if comparators.is_empty() {
62 return Err(ShellError::Generic(GenericError::new(
63 "expected name",
64 "requires a cell path or closure to sort data",
65 head_span,
66 )));
67 }
68
69 let mut compare_err: Option<ShellError> = None;
73
74 vec.sort_by(|a, b| {
75 compare_by(
76 a,
77 b,
78 &mut comparators,
79 head_span,
80 insensitive,
81 natural,
82 &mut compare_err,
83 )
84 });
85
86 if let Some(err) = compare_err {
87 Err(err)
88 } else {
89 Ok(())
90 }
91}
92
93pub fn sort_record(
97 record: Record,
98 sort_by_value: bool,
99 reverse: bool,
100 insensitive: bool,
101 natural: bool,
102) -> Result<Record, ShellError> {
103 let mut input_pairs: Vec<(String, Value)> = record.into_iter().collect();
104
105 let mut compare_err: Option<ShellError> = None;
109
110 if sort_by_value {
111 input_pairs.sort_by(|a, b| {
112 if compare_err.is_some() {
114 return Ordering::Equal;
115 }
116
117 compare_values(&a.1, &b.1, insensitive, natural).unwrap_or_else(|err| {
118 compare_err.get_or_insert(err);
119 Ordering::Equal
120 })
121 });
122 } else {
123 input_pairs.sort_by(|a, b| compare_strings(&a.0, &b.0, insensitive, natural));
124 };
125
126 if let Some(err) = compare_err {
127 return Err(err);
128 }
129
130 if reverse {
131 input_pairs.reverse()
132 }
133
134 Ok(input_pairs.into_iter().collect())
135}
136
137pub fn compare_by(
138 left: &Value,
139 right: &Value,
140 comparators: &mut [Comparator],
141 span: Span,
142 insensitive: bool,
143 natural: bool,
144 error: &mut Option<ShellError>,
145) -> Ordering {
146 if error.is_some() {
148 return Ordering::Equal;
149 }
150 for cmp in comparators.iter_mut() {
151 let result = match cmp {
152 Comparator::CellPath(cell_path) => {
153 compare_cell_path(left, right, cell_path, insensitive, natural)
154 }
155 Comparator::KeyClosure(closure) => {
156 compare_key_closure(left, right, closure, span, insensitive, natural)
157 }
158 Comparator::CustomClosure(closure) => {
159 compare_custom_closure(left, right, closure, span)
160 }
161 };
162 match result {
163 Ok(Ordering::Equal) => {}
164 Ok(ordering) => return ordering,
165 Err(err) => {
166 error.get_or_insert(err);
169 return Ordering::Equal;
170 }
171 }
172 }
173 Ordering::Equal
174}
175
176fn should_sort_as_string(val: &Value, natural: bool) -> bool {
181 matches!(
182 (val, natural),
183 (&Value::String { .. }, _)
184 | (&Value::Glob { .. }, _)
185 | (&Value::Int { .. }, true)
186 | (&Value::Float { .. }, true)
187 )
188}
189
190fn should_string_compare(left: &Value, right: &Value, natural: bool) -> bool {
193 should_sort_as_string(left, natural) && should_sort_as_string(right, natural)
194}
195
196pub fn compare_values(
197 left: &Value,
198 right: &Value,
199 insensitive: bool,
200 natural: bool,
201) -> Result<Ordering, ShellError> {
202 if should_string_compare(left, right, natural) {
203 Ok(compare_strings(
204 &left.coerce_str()?,
205 &right.coerce_str()?,
206 insensitive,
207 natural,
208 ))
209 } else {
210 Ok(left.partial_cmp(right).unwrap_or(Ordering::Equal))
211 }
212}
213
214pub fn compare_strings(left: &str, right: &str, insensitive: bool, natural: bool) -> Ordering {
215 fn compare_inner<T>(left: T, right: T, natural: bool) -> Ordering
216 where
217 T: AsRef<str> + Ord,
218 {
219 if natural {
220 alphanumeric_sort::compare_str(left, right)
221 } else {
222 left.cmp(&right)
223 }
224 }
225
226 if insensitive {
228 compare_inner(left.to_folded_case(), right.to_folded_case(), natural)
229 } else {
230 compare_inner(left, right, natural)
231 }
232}
233
234pub fn compare_cell_path(
235 left: &Value,
236 right: &Value,
237 cell_path: &CellPath,
238 insensitive: bool,
239 natural: bool,
240) -> Result<Ordering, ShellError> {
241 let left = left.follow_cell_path(&cell_path.members)?;
242 let right = right.follow_cell_path(&cell_path.members)?;
243 compare_values(&left, &right, insensitive, natural)
244}
245
246pub fn compare_key_closure(
247 left: &Value,
248 right: &Value,
249 closure_eval: &mut ClosureEval,
250 span: Span,
251 insensitive: bool,
252 natural: bool,
253) -> Result<Ordering, ShellError> {
254 let left_key = closure_eval
255 .run_with_value(left.clone())?
256 .into_value(span)?;
257 let right_key = closure_eval
258 .run_with_value(right.clone())?
259 .into_value(span)?;
260 compare_values(&left_key, &right_key, insensitive, natural)
261}
262
263pub fn compare_custom_closure(
264 left: &Value,
265 right: &Value,
266 closure_eval: &mut ClosureEval,
267 span: Span,
268) -> Result<Ordering, ShellError> {
269 closure_eval
270 .add_arg(left.clone())
271 .add_arg(right.clone())
272 .run_with_input(PipelineData::value(
273 Value::list(vec![left.clone(), right.clone()], span),
274 None,
275 ))
276 .and_then(|data| data.into_value(span))
277 .map(|val| {
278 if val.is_true() {
279 Ordering::Less
280 } else {
281 Ordering::Greater
282 }
283 })
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289 use nu_protocol::{ast::PathMember, casing::Casing, record};
290
291 #[test]
292 fn test_sort_basic() {
293 let mut list = vec![
294 Value::test_string("foo"),
295 Value::test_int(2),
296 Value::test_int(3),
297 Value::test_string("bar"),
298 Value::test_int(1),
299 Value::test_string("baz"),
300 ];
301
302 assert!(sort(&mut list, false, false).is_ok());
303 assert_eq!(
304 list,
305 vec![
306 Value::test_int(1),
307 Value::test_int(2),
308 Value::test_int(3),
309 Value::test_string("bar"),
310 Value::test_string("baz"),
311 Value::test_string("foo")
312 ]
313 );
314 }
315
316 #[test]
317 fn test_sort_nothing() {
318 let mut list = vec![
320 Value::test_int(1),
321 Value::test_nothing(),
322 Value::test_int(2),
323 Value::test_string("foo"),
324 Value::test_nothing(),
325 Value::test_string("bar"),
326 ];
327
328 assert!(sort(&mut list, false, false).is_ok());
329 assert_eq!(
330 list,
331 vec![
332 Value::test_int(1),
333 Value::test_int(2),
334 Value::test_string("bar"),
335 Value::test_string("foo"),
336 Value::test_nothing(),
337 Value::test_nothing()
338 ]
339 );
340
341 let mut values: Vec<Value> =
347 itertools::intersperse(Value::test_values(), Value::test_nothing()).collect();
348
349 let nulls = values
350 .iter()
351 .filter(|item| item == &&Value::test_nothing())
352 .count();
353
354 assert!(sort(&mut values, false, false).is_ok());
355
356 assert_eq!(&values[(nulls - 1)..], vec![Value::test_nothing(); nulls])
358 }
359
360 #[test]
361 fn test_sort_natural_basic() {
362 let mut list = vec![
363 Value::test_string("foo99"),
364 Value::test_string("foo9"),
365 Value::test_string("foo1"),
366 Value::test_string("foo100"),
367 Value::test_string("foo10"),
368 Value::test_string("1"),
369 Value::test_string("10"),
370 Value::test_string("100"),
371 Value::test_string("9"),
372 Value::test_string("99"),
373 ];
374
375 assert!(sort(&mut list, false, false).is_ok());
376 assert_eq!(
377 list,
378 vec![
379 Value::test_string("1"),
380 Value::test_string("10"),
381 Value::test_string("100"),
382 Value::test_string("9"),
383 Value::test_string("99"),
384 Value::test_string("foo1"),
385 Value::test_string("foo10"),
386 Value::test_string("foo100"),
387 Value::test_string("foo9"),
388 Value::test_string("foo99"),
389 ]
390 );
391
392 assert!(sort(&mut list, false, true).is_ok());
393 assert_eq!(
394 list,
395 vec![
396 Value::test_string("1"),
397 Value::test_string("9"),
398 Value::test_string("10"),
399 Value::test_string("99"),
400 Value::test_string("100"),
401 Value::test_string("foo1"),
402 Value::test_string("foo9"),
403 Value::test_string("foo10"),
404 Value::test_string("foo99"),
405 Value::test_string("foo100"),
406 ]
407 );
408 }
409
410 #[test]
411 fn test_sort_natural_mixed_types() {
412 let mut list = vec![
413 Value::test_string("1"),
414 Value::test_int(99),
415 Value::test_int(1),
416 Value::test_float(1000.0),
417 Value::test_int(9),
418 Value::test_string("9"),
419 Value::test_int(100),
420 Value::test_string("99"),
421 Value::test_float(2.0),
422 Value::test_string("100"),
423 Value::test_int(10),
424 Value::test_string("10"),
425 ];
426
427 assert!(sort(&mut list, false, false).is_ok());
428 assert_eq!(
429 list,
430 vec![
431 Value::test_int(1),
432 Value::test_float(2.0),
433 Value::test_int(9),
434 Value::test_int(10),
435 Value::test_int(99),
436 Value::test_int(100),
437 Value::test_float(1000.0),
438 Value::test_string("1"),
439 Value::test_string("10"),
440 Value::test_string("100"),
441 Value::test_string("9"),
442 Value::test_string("99")
443 ]
444 );
445
446 assert!(sort(&mut list, false, true).is_ok());
447 assert_eq!(
448 list,
449 vec![
450 Value::test_int(1),
451 Value::test_string("1"),
452 Value::test_float(2.0),
453 Value::test_int(9),
454 Value::test_string("9"),
455 Value::test_int(10),
456 Value::test_string("10"),
457 Value::test_int(99),
458 Value::test_string("99"),
459 Value::test_int(100),
460 Value::test_string("100"),
461 Value::test_float(1000.0),
462 ]
463 );
464 }
465
466 #[test]
467 fn test_sort_natural_no_numeric_values() {
468 let mut normal = vec![
471 Value::test_string("golf"),
472 Value::test_bool(false),
473 Value::test_string("alfa"),
474 Value::test_string("echo"),
475 Value::test_int(7),
476 Value::test_int(10),
477 Value::test_bool(true),
478 Value::test_string("uniform"),
479 Value::test_int(3),
480 Value::test_string("tango"),
481 ];
482 let mut natural = normal.clone();
483
484 assert!(sort(&mut normal, false, false).is_ok());
485 assert!(sort(&mut natural, false, true).is_ok());
486 assert_eq!(normal, natural);
487 }
488
489 #[test]
490 fn test_sort_natural_type_order() {
491 let mut list = vec![
512 Value::test_string("golf"),
513 Value::test_int(1),
514 Value::test_bool(false),
515 Value::test_string("alfa"),
516 Value::test_int(7),
517 Value::test_int(10),
518 Value::test_bool(true),
519 Value::test_string("uniform"),
520 Value::test_bool(true),
521 Value::test_int(3),
522 Value::test_bool(false),
523 Value::test_string("tango"),
524 ];
525
526 assert!(sort(&mut list, false, true).is_ok());
527 assert_eq!(
528 list,
529 vec![
530 Value::test_bool(false),
531 Value::test_bool(false),
532 Value::test_bool(true),
533 Value::test_bool(true),
534 Value::test_int(1),
535 Value::test_int(3),
536 Value::test_int(7),
537 Value::test_int(10),
538 Value::test_string("alfa"),
539 Value::test_string("golf"),
540 Value::test_string("tango"),
541 Value::test_string("uniform")
542 ]
543 );
544
545 let year_three = chrono::NaiveDate::from_ymd_opt(3, 1, 1)
551 .unwrap()
552 .and_hms_opt(0, 0, 0)
553 .unwrap()
554 .and_utc();
555
556 let mut list = vec![
557 Value::test_int(10),
558 Value::test_float(6.0),
559 Value::test_int(1),
560 Value::test_binary([3]),
561 Value::test_string("2"),
562 Value::test_date(year_three.into()),
563 Value::test_int(4),
564 Value::test_binary([52]),
565 Value::test_float(9.0),
566 Value::test_string("5"),
567 Value::test_date(chrono::DateTime::UNIX_EPOCH.into()),
568 Value::test_int(7),
569 Value::test_string("8"),
570 Value::test_float(3.0),
571 Value::test_string("foobar"),
572 ];
573 assert!(sort(&mut list, false, true).is_ok());
574 assert_eq!(
575 list,
576 vec![
577 Value::test_int(1),
578 Value::test_string("2"),
579 Value::test_float(3.0),
580 Value::test_int(4),
581 Value::test_string("5"),
582 Value::test_float(6.0),
583 Value::test_int(7),
584 Value::test_string("8"),
585 Value::test_float(9.0),
586 Value::test_int(10),
587 Value::test_string("foobar"),
588 Value::test_date(year_three.into()),
591 Value::test_date(chrono::DateTime::UNIX_EPOCH.into()),
592 Value::test_binary([3]),
593 Value::test_binary([52]),
594 ]
595 );
596 }
597
598 #[test]
599 fn test_sort_insensitive() {
600 let source = vec![
604 Value::test_string("FOO"),
605 Value::test_string("foo"),
606 Value::test_int(100),
607 Value::test_string("9"),
608 Value::test_string("bar"),
609 Value::test_int(10),
610 Value::test_string("baz"),
611 Value::test_string("BAR"),
612 ];
613 let mut list;
614
615 list = source.clone();
617 assert!(sort(&mut list, false, false).is_ok());
618 assert_eq!(
619 list,
620 vec![
621 Value::test_int(10),
622 Value::test_int(100),
623 Value::test_string("9"),
624 Value::test_string("BAR"),
625 Value::test_string("FOO"),
626 Value::test_string("bar"),
627 Value::test_string("baz"),
628 Value::test_string("foo"),
629 ]
630 );
631
632 list = source.clone();
634 assert!(sort(&mut list, false, true).is_ok());
635 assert_eq!(
636 list,
637 vec![
638 Value::test_string("9"),
639 Value::test_int(10),
640 Value::test_int(100),
641 Value::test_string("BAR"),
642 Value::test_string("FOO"),
643 Value::test_string("bar"),
644 Value::test_string("baz"),
645 Value::test_string("foo"),
646 ]
647 );
648
649 list = source.clone();
651 assert!(sort(&mut list, true, false).is_ok());
652 assert_eq!(
653 list,
654 vec![
655 Value::test_int(10),
656 Value::test_int(100),
657 Value::test_string("9"),
658 Value::test_string("bar"),
659 Value::test_string("BAR"),
660 Value::test_string("baz"),
661 Value::test_string("FOO"),
662 Value::test_string("foo"),
663 ]
664 );
665
666 list = source.clone();
668 assert!(sort(&mut list, true, true).is_ok());
669 assert_eq!(
670 list,
671 vec![
672 Value::test_string("9"),
673 Value::test_int(10),
674 Value::test_int(100),
675 Value::test_string("bar"),
676 Value::test_string("BAR"),
677 Value::test_string("baz"),
678 Value::test_string("FOO"),
679 Value::test_string("foo"),
680 ]
681 );
682 }
683
684 fn assert_record_eq(a: Record, b: Record) {
687 assert_eq!(
688 a.into_iter().collect::<Vec<_>>(),
689 b.into_iter().collect::<Vec<_>>(),
690 )
691 }
692
693 #[test]
694 fn test_sort_record_keys() {
695 let record = record! {
697 "golf" => Value::test_string("bar"),
698 "alfa" => Value::test_string("foo"),
699 "echo" => Value::test_int(123),
700 };
701
702 let sorted = sort_record(record, false, false, false, false).unwrap();
703 assert_record_eq(
704 sorted,
705 record! {
706 "alfa" => Value::test_string("foo"),
707 "echo" => Value::test_int(123),
708 "golf" => Value::test_string("bar"),
709 },
710 );
711 }
712
713 #[test]
714 fn test_sort_record_values() {
715 let record = record! {
733 "alfa" => Value::test_string("1"),
734 "bravo" => Value::test_int(99),
735 "charlie" => Value::test_int(1),
736 "delta" => Value::test_int(9),
737 "echo" => Value::test_string("9"),
738 "foxtrot" => Value::test_int(100),
739 "golf" => Value::test_string("99"),
740 "hotel" => Value::test_string("100"),
741 "india" => Value::test_int(10),
742 "juliett" => Value::test_string("10"),
743 };
744
745 let sorted = sort_record(record.clone(), true, false, false, false).unwrap();
747 assert_record_eq(
748 sorted,
749 record! {
750 "charlie" => Value::test_int(1),
751 "delta" => Value::test_int(9),
752 "india" => Value::test_int(10),
753 "bravo" => Value::test_int(99),
754 "foxtrot" => Value::test_int(100),
755 "alfa" => Value::test_string("1"),
756 "juliett" => Value::test_string("10"),
757 "hotel" => Value::test_string("100"),
758 "echo" => Value::test_string("9"),
759 "golf" => Value::test_string("99"),
760 },
761 );
762
763 let sorted = sort_record(record.clone(), true, false, false, true).unwrap();
765 assert_record_eq(
766 sorted,
767 record! {
768 "alfa" => Value::test_string("1"),
769 "charlie" => Value::test_int(1),
770 "delta" => Value::test_int(9),
771 "echo" => Value::test_string("9"),
772 "india" => Value::test_int(10),
773 "juliett" => Value::test_string("10"),
774 "bravo" => Value::test_int(99),
775 "golf" => Value::test_string("99"),
776 "foxtrot" => Value::test_int(100),
777 "hotel" => Value::test_string("100"),
778 },
779 );
780 }
781
782 #[test]
783 fn test_sort_equivalent() {
784 let phonetic = vec![
786 "alfa", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", "india",
787 "juliett", "kilo", "lima", "mike", "november", "oscar", "papa", "quebec", "romeo",
788 "sierra", "tango", "uniform", "victor", "whiskey", "xray", "yankee", "zulu",
789 ];
790
791 let mut values: Vec<Value> = Value::test_values()
793 .into_iter()
794 .filter(|val| !matches!(val, Value::Error { .. }))
795 .collect();
796
797 values.sort_by(|a, b| b.partial_cmp(a).unwrap());
799
800 let mut list = values.clone();
801 let mut table: Vec<Value> = values
802 .clone()
803 .into_iter()
804 .map(|val| Value::test_record(record! { "value" => val }))
805 .collect();
806 let record = Record::from_iter(phonetic.into_iter().map(str::to_string).zip(values));
807
808 let comparator = Comparator::CellPath(CellPath {
809 members: vec![PathMember::String {
810 val: "value".to_string(),
811 span: Span::test_data(),
812 optional: false,
813 casing: Casing::Sensitive,
814 }],
815 });
816
817 assert!(sort(&mut list, false, false).is_ok());
818 assert!(
819 sort_by(
820 &mut table,
821 vec![comparator],
822 Span::test_data(),
823 false,
824 false
825 )
826 .is_ok()
827 );
828
829 let record_sorted = sort_record(record.clone(), true, false, false, false).unwrap();
830 let record_vals: Vec<Value> = record_sorted.into_iter().map(|pair| pair.1).collect();
831
832 let table_vals: Vec<Value> = table
833 .clone()
834 .into_iter()
835 .map(|record| record.into_record().unwrap().remove("value").unwrap())
836 .collect();
837
838 assert_eq!(list, record_vals);
839 assert_eq!(record_vals, table_vals);
840 }
842}