1use nu_engine::ClosureEval;
2use nu_protocol::{PipelineData, Record, ShellError, Span, Value, ast::CellPath};
3use nu_utils::IgnoreCaseExt;
4use std::cmp::Ordering;
5
6pub enum Comparator {
11 KeyClosure(ClosureEval),
12 CustomClosure(ClosureEval),
13 CellPath(CellPath),
14}
15
16pub fn sort(vec: &mut [Value], insensitive: bool, natural: bool) -> Result<(), ShellError> {
28 let mut compare_err: Option<ShellError> = None;
32
33 vec.sort_by(|a, b| {
34 if compare_err.is_some() {
36 return Ordering::Equal;
37 }
38
39 compare_values(a, b, insensitive, natural).unwrap_or_else(|err| {
40 compare_err.get_or_insert(err);
41 Ordering::Equal
42 })
43 });
44
45 if let Some(err) = compare_err {
46 Err(err)
47 } else {
48 Ok(())
49 }
50}
51
52pub fn sort_by(
54 vec: &mut [Value],
55 mut comparators: Vec<Comparator>,
56 head_span: Span,
57 insensitive: bool,
58 natural: bool,
59) -> Result<(), ShellError> {
60 if comparators.is_empty() {
61 return Err(ShellError::GenericError {
62 error: "expected name".into(),
63 msg: "requires a cell path or closure to sort data".into(),
64 span: Some(head_span),
65 help: None,
66 inner: vec![],
67 });
68 }
69
70 let mut compare_err: Option<ShellError> = None;
74
75 vec.sort_by(|a, b| {
76 compare_by(
77 a,
78 b,
79 &mut comparators,
80 head_span,
81 insensitive,
82 natural,
83 &mut compare_err,
84 )
85 });
86
87 if let Some(err) = compare_err {
88 Err(err)
89 } else {
90 Ok(())
91 }
92}
93
94pub fn sort_record(
98 record: Record,
99 sort_by_value: bool,
100 reverse: bool,
101 insensitive: bool,
102 natural: bool,
103) -> Result<Record, ShellError> {
104 let mut input_pairs: Vec<(String, Value)> = record.into_iter().collect();
105
106 let mut compare_err: Option<ShellError> = None;
110
111 if sort_by_value {
112 input_pairs.sort_by(|a, b| {
113 if compare_err.is_some() {
115 return Ordering::Equal;
116 }
117
118 compare_values(&a.1, &b.1, insensitive, natural).unwrap_or_else(|err| {
119 compare_err.get_or_insert(err);
120 Ordering::Equal
121 })
122 });
123 } else {
124 input_pairs.sort_by(|a, b| compare_strings(&a.0, &b.0, insensitive, natural));
125 };
126
127 if let Some(err) = compare_err {
128 return Err(err);
129 }
130
131 if reverse {
132 input_pairs.reverse()
133 }
134
135 Ok(input_pairs.into_iter().collect())
136}
137
138pub fn compare_by(
139 left: &Value,
140 right: &Value,
141 comparators: &mut [Comparator],
142 span: Span,
143 insensitive: bool,
144 natural: bool,
145 error: &mut Option<ShellError>,
146) -> Ordering {
147 if error.is_some() {
149 return Ordering::Equal;
150 }
151 for cmp in comparators.iter_mut() {
152 let result = match cmp {
153 Comparator::CellPath(cell_path) => {
154 compare_cell_path(left, right, cell_path, insensitive, natural)
155 }
156 Comparator::KeyClosure(closure) => {
157 compare_key_closure(left, right, closure, span, insensitive, natural)
158 }
159 Comparator::CustomClosure(closure) => {
160 compare_custom_closure(left, right, closure, span)
161 }
162 };
163 match result {
164 Ok(Ordering::Equal) => {}
165 Ok(ordering) => return ordering,
166 Err(err) => {
167 error.get_or_insert(err);
170 return Ordering::Equal;
171 }
172 }
173 }
174 Ordering::Equal
175}
176
177fn should_sort_as_string(val: &Value, natural: bool) -> bool {
182 matches!(
183 (val, natural),
184 (&Value::String { .. }, _)
185 | (&Value::Glob { .. }, _)
186 | (&Value::Int { .. }, true)
187 | (&Value::Float { .. }, true)
188 )
189}
190
191fn should_string_compare(left: &Value, right: &Value, natural: bool) -> bool {
194 should_sort_as_string(left, natural) && should_sort_as_string(right, natural)
195}
196
197pub fn compare_values(
198 left: &Value,
199 right: &Value,
200 insensitive: bool,
201 natural: bool,
202) -> Result<Ordering, ShellError> {
203 if should_string_compare(left, right, natural) {
204 Ok(compare_strings(
205 &left.coerce_str()?,
206 &right.coerce_str()?,
207 insensitive,
208 natural,
209 ))
210 } else {
211 Ok(left.partial_cmp(right).unwrap_or(Ordering::Equal))
212 }
213}
214
215pub fn compare_strings(left: &str, right: &str, insensitive: bool, natural: bool) -> Ordering {
216 fn compare_inner<T>(left: T, right: T, natural: bool) -> Ordering
217 where
218 T: AsRef<str> + Ord,
219 {
220 if natural {
221 alphanumeric_sort::compare_str(left, right)
222 } else {
223 left.cmp(&right)
224 }
225 }
226
227 if insensitive {
229 compare_inner(left.to_folded_case(), right.to_folded_case(), natural)
230 } else {
231 compare_inner(left, right, natural)
232 }
233}
234
235pub fn compare_cell_path(
236 left: &Value,
237 right: &Value,
238 cell_path: &CellPath,
239 insensitive: bool,
240 natural: bool,
241) -> Result<Ordering, ShellError> {
242 let left = left.follow_cell_path(&cell_path.members)?;
243 let right = right.follow_cell_path(&cell_path.members)?;
244 compare_values(&left, &right, insensitive, natural)
245}
246
247pub fn compare_key_closure(
248 left: &Value,
249 right: &Value,
250 closure_eval: &mut ClosureEval,
251 span: Span,
252 insensitive: bool,
253 natural: bool,
254) -> Result<Ordering, ShellError> {
255 let left_key = closure_eval
256 .run_with_value(left.clone())?
257 .into_value(span)?;
258 let right_key = closure_eval
259 .run_with_value(right.clone())?
260 .into_value(span)?;
261 compare_values(&left_key, &right_key, insensitive, natural)
262}
263
264pub fn compare_custom_closure(
265 left: &Value,
266 right: &Value,
267 closure_eval: &mut ClosureEval,
268 span: Span,
269) -> Result<Ordering, ShellError> {
270 closure_eval
271 .add_arg(left.clone())
272 .add_arg(right.clone())
273 .run_with_input(PipelineData::Value(
274 Value::list(vec![left.clone(), right.clone()], span),
275 None,
276 ))
277 .and_then(|data| data.into_value(span))
278 .map(|val| {
279 if val.is_true() {
280 Ordering::Less
281 } else {
282 Ordering::Greater
283 }
284 })
285}