1use crate::builtins::common::spec::{
4 BroadcastSemantics, BuiltinFusionSpec, BuiltinGpuSpec, ConstantStrategy, GpuOpKind,
5 ReductionNaN, ResidencyPolicy, ShapeRequirements,
6};
7use crate::builtins::common::tensor;
8use crate::builtins::structs::type_resolvers::orderfields_type;
9
10use runmat_builtins::{
11 BuiltinCompletionPolicy, BuiltinDescriptor, BuiltinErrorDescriptor, BuiltinOutputMode,
12 BuiltinParamArity, BuiltinParamDescriptor, BuiltinParamType, BuiltinSignatureDescriptor,
13 CellArray, StructValue, Tensor, Value,
14};
15use runmat_macros::runtime_builtin;
16use std::cmp::Ordering;
17use std::collections::{HashMap, HashSet};
18
19use crate::{build_runtime_error, BuiltinResult, RuntimeError};
20
21#[runmat_macros::register_gpu_spec(builtin_path = "crate::builtins::structs::core::orderfields")]
22pub const GPU_SPEC: BuiltinGpuSpec = BuiltinGpuSpec {
23 name: "orderfields",
24 op_kind: GpuOpKind::Custom("orderfields"),
25 supported_precisions: &[],
26 broadcast: BroadcastSemantics::None,
27 provider_hooks: &[],
28 constant_strategy: ConstantStrategy::InlineLiteral,
29 residency: ResidencyPolicy::InheritInputs,
30 nan_mode: ReductionNaN::Include,
31 two_pass_threshold: None,
32 workgroup_size: None,
33 accepts_nan_mode: false,
34 notes: "Host-only metadata manipulation; struct values that live on the GPU remain resident.",
35};
36
37#[runmat_macros::register_fusion_spec(builtin_path = "crate::builtins::structs::core::orderfields")]
38pub const FUSION_SPEC: BuiltinFusionSpec = BuiltinFusionSpec {
39 name: "orderfields",
40 shape: ShapeRequirements::Any,
41 constant_strategy: ConstantStrategy::InlineLiteral,
42 elementwise: None,
43 reduction: None,
44 emits_nan: false,
45 notes: "Reordering fields is a metadata operation and does not participate in fusion planning.",
46};
47
48const BUILTIN_NAME: &str = "orderfields";
49
50const ORDERFIELDS_OUTPUT_ORDERED: [BuiltinParamDescriptor; 1] = [BuiltinParamDescriptor {
51 name: "S",
52 ty: BuiltinParamType::Any,
53 arity: BuiltinParamArity::Required,
54 default: None,
55 description: "Struct or struct array with reordered fields.",
56}];
57
58const ORDERFIELDS_OUTPUT_PERM: [BuiltinParamDescriptor; 2] = [
59 BuiltinParamDescriptor {
60 name: "S",
61 ty: BuiltinParamType::Any,
62 arity: BuiltinParamArity::Required,
63 default: None,
64 description: "Struct or struct array with reordered fields.",
65 },
66 BuiltinParamDescriptor {
67 name: "P",
68 ty: BuiltinParamType::NumericArray,
69 arity: BuiltinParamArity::Required,
70 default: None,
71 description: "Permutation vector mapping ordered fields to original positions.",
72 },
73];
74
75const ORDERFIELDS_INPUTS_ONE: [BuiltinParamDescriptor; 1] = [BuiltinParamDescriptor {
76 name: "S",
77 ty: BuiltinParamType::Any,
78 arity: BuiltinParamArity::Required,
79 default: None,
80 description: "Input struct or struct array.",
81}];
82
83const ORDERFIELDS_INPUTS_TWO: [BuiltinParamDescriptor; 2] = [
84 BuiltinParamDescriptor {
85 name: "S",
86 ty: BuiltinParamType::Any,
87 arity: BuiltinParamArity::Required,
88 default: None,
89 description: "Input struct or struct array.",
90 },
91 BuiltinParamDescriptor {
92 name: "order",
93 ty: BuiltinParamType::Any,
94 arity: BuiltinParamArity::Optional,
95 default: None,
96 description: "Reference struct, field-name list, or permutation vector.",
97 },
98];
99
100const ORDERFIELDS_SIGNATURES: [BuiltinSignatureDescriptor; 4] = [
101 BuiltinSignatureDescriptor {
102 label: "S = orderfields(S)",
103 inputs: &ORDERFIELDS_INPUTS_ONE,
104 outputs: &ORDERFIELDS_OUTPUT_ORDERED,
105 },
106 BuiltinSignatureDescriptor {
107 label: "S = orderfields(S, order)",
108 inputs: &ORDERFIELDS_INPUTS_TWO,
109 outputs: &ORDERFIELDS_OUTPUT_ORDERED,
110 },
111 BuiltinSignatureDescriptor {
112 label: "[S,P] = orderfields(S)",
113 inputs: &ORDERFIELDS_INPUTS_ONE,
114 outputs: &ORDERFIELDS_OUTPUT_PERM,
115 },
116 BuiltinSignatureDescriptor {
117 label: "[S,P] = orderfields(S, order)",
118 inputs: &ORDERFIELDS_INPUTS_TWO,
119 outputs: &ORDERFIELDS_OUTPUT_PERM,
120 },
121];
122
123const ORDERFIELDS_ERROR_TOO_MANY_INPUTS: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
124 code: "RM.ORDERFIELDS.TOO_MANY_INPUTS",
125 identifier: Some("orderfields:TooManyInputs"),
126 when: "More than two input arguments are supplied.",
127 message: "orderfields: expected at most two input arguments",
128};
129
130const ORDERFIELDS_ERROR_INVALID_INPUT: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
131 code: "RM.ORDERFIELDS.INVALID_INPUT",
132 identifier: Some("orderfields:InvalidInput"),
133 when: "First argument is not a struct or struct array.",
134 message: "orderfields: first argument must be a struct or struct array",
135};
136
137const ORDERFIELDS_ERROR_EMPTY_STRUCT_ARRAY: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
138 code: "RM.ORDERFIELDS.EMPTY_STRUCT_ARRAY",
139 identifier: Some("orderfields:EmptyStructArray"),
140 when: "Empty struct array is asked to adopt a non-empty reference order.",
141 message: "orderfields: empty struct arrays cannot adopt a non-empty reference order",
142};
143
144const ORDERFIELDS_ERROR_NO_FIELDS: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
145 code: "RM.ORDERFIELDS.NO_FIELDS",
146 identifier: Some("orderfields:NoFields"),
147 when: "Ordering is requested for a struct array with no fields.",
148 message: "orderfields: struct array has no fields to reorder",
149};
150
151const ORDERFIELDS_ERROR_INVALID_STRUCT_ARRAY: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
152 code: "RM.ORDERFIELDS.INVALID_STRUCT_ARRAY",
153 identifier: Some("orderfields:InvalidStructArray"),
154 when: "A struct-array element is not a struct.",
155 message: "orderfields: struct array element is not a struct",
156};
157
158const ORDERFIELDS_ERROR_INVALID_STRUCT_CONTENTS: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
159 code: "RM.ORDERFIELDS.INVALID_STRUCT_CONTENTS",
160 identifier: Some("orderfields:InvalidStructContents"),
161 when: "Struct-array contents are not all structs.",
162 message: "orderfields: expected struct array contents to be structs",
163};
164
165const ORDERFIELDS_ERROR_REBUILD_FAILED: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
166 code: "RM.ORDERFIELDS.REBUILD_FAILED",
167 identifier: Some("orderfields:RebuildFailed"),
168 when: "Rebuilding reordered struct array failed.",
169 message: "orderfields: failed to rebuild struct array",
170};
171
172const ORDERFIELDS_ERROR_INVALID_REFERENCE: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
173 code: "RM.ORDERFIELDS.INVALID_REFERENCE",
174 identifier: Some("orderfields:InvalidReference"),
175 when: "Reference struct-array contains non-struct values.",
176 message: "orderfields: reference struct array element is not a struct",
177};
178
179const ORDERFIELDS_ERROR_INVALID_NAME_LIST: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
180 code: "RM.ORDERFIELDS.INVALID_NAME_LIST",
181 identifier: Some("orderfields:InvalidFieldNameList"),
182 when: "Name-list entries are not scalar strings or character vectors.",
183 message: "orderfields: cell array elements must be a string or character vector",
184};
185
186const ORDERFIELDS_ERROR_EMPTY_FIELD_NAME: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
187 code: "RM.ORDERFIELDS.EMPTY_FIELD_NAME",
188 identifier: Some("orderfields:EmptyFieldName"),
189 when: "Requested field-name list contains an empty name.",
190 message: "orderfields: field names must be nonempty",
191};
192
193const ORDERFIELDS_ERROR_INVALID_PERMUTATION: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
194 code: "RM.ORDERFIELDS.INVALID_PERMUTATION",
195 identifier: Some("orderfields:InvalidPermutation"),
196 when: "Permutation vector does not include each field exactly once.",
197 message: "orderfields: index vector must permute every field exactly once",
198};
199
200const ORDERFIELDS_ERROR_INDEX_NOT_INTEGER: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
201 code: "RM.ORDERFIELDS.INDEX_NOT_INTEGER",
202 identifier: Some("orderfields:IndexNotInteger"),
203 when: "Permutation vector contains non-integer entries.",
204 message: "orderfields: index vector must contain integers",
205};
206
207const ORDERFIELDS_ERROR_INDEX_OUT_OF_RANGE: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
208 code: "RM.ORDERFIELDS.INDEX_OUT_OF_RANGE",
209 identifier: Some("orderfields:IndexOutOfRange"),
210 when: "Permutation vector index is outside valid field range.",
211 message: "orderfields: index vector element out of range",
212};
213
214const ORDERFIELDS_ERROR_INDEX_DUPLICATE: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
215 code: "RM.ORDERFIELDS.INDEX_DUPLICATE",
216 identifier: Some("orderfields:DuplicateIndex"),
217 when: "Permutation vector contains duplicate positions.",
218 message: "orderfields: index vector contains duplicate positions",
219};
220
221const ORDERFIELDS_ERROR_FIELD_MISMATCH: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
222 code: "RM.ORDERFIELDS.FIELD_MISMATCH",
223 identifier: Some("orderfields:FieldMismatch"),
224 when: "Requested field set does not exactly match struct fields.",
225 message: "orderfields: field names must match the struct exactly",
226};
227
228const ORDERFIELDS_ERROR_UNKNOWN_FIELD: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
229 code: "RM.ORDERFIELDS.UNKNOWN_FIELD",
230 identifier: Some("orderfields:UnknownField"),
231 when: "Requested order references an unknown field.",
232 message: "orderfields: unknown field in requested order",
233};
234
235const ORDERFIELDS_ERROR_DUPLICATE_FIELD: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
236 code: "RM.ORDERFIELDS.DUPLICATE_FIELD",
237 identifier: Some("orderfields:DuplicateField"),
238 when: "Requested field order includes duplicate names.",
239 message: "orderfields: duplicate field in requested order",
240};
241
242const ORDERFIELDS_ERROR_MISSING_FIELD: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
243 code: "RM.ORDERFIELDS.MISSING_FIELD",
244 identifier: Some("orderfields:MissingField"),
245 when: "A field from the requested order is missing on the struct.",
246 message: "orderfields: field does not exist on the struct",
247};
248
249const ORDERFIELDS_ERROR_INVALID_ORDER_ARGUMENT: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
250 code: "RM.ORDERFIELDS.INVALID_ORDER_ARGUMENT",
251 identifier: Some("orderfields:InvalidOrderArgument"),
252 when: "Second argument is not a supported order descriptor.",
253 message: "orderfields: unrecognised ordering argument",
254};
255
256const ORDERFIELDS_ERROR_INTERNAL: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
257 code: "RM.ORDERFIELDS.INTERNAL",
258 identifier: Some("orderfields:InternalError"),
259 when: "Internal permutation tensor construction failed.",
260 message: "orderfields: internal error",
261};
262
263const ORDERFIELDS_ERRORS: [BuiltinErrorDescriptor; 20] = [
264 ORDERFIELDS_ERROR_TOO_MANY_INPUTS,
265 ORDERFIELDS_ERROR_INVALID_INPUT,
266 ORDERFIELDS_ERROR_EMPTY_STRUCT_ARRAY,
267 ORDERFIELDS_ERROR_NO_FIELDS,
268 ORDERFIELDS_ERROR_INVALID_STRUCT_ARRAY,
269 ORDERFIELDS_ERROR_INVALID_STRUCT_CONTENTS,
270 ORDERFIELDS_ERROR_REBUILD_FAILED,
271 ORDERFIELDS_ERROR_INVALID_REFERENCE,
272 ORDERFIELDS_ERROR_INVALID_NAME_LIST,
273 ORDERFIELDS_ERROR_EMPTY_FIELD_NAME,
274 ORDERFIELDS_ERROR_INVALID_PERMUTATION,
275 ORDERFIELDS_ERROR_INDEX_NOT_INTEGER,
276 ORDERFIELDS_ERROR_INDEX_OUT_OF_RANGE,
277 ORDERFIELDS_ERROR_INDEX_DUPLICATE,
278 ORDERFIELDS_ERROR_FIELD_MISMATCH,
279 ORDERFIELDS_ERROR_UNKNOWN_FIELD,
280 ORDERFIELDS_ERROR_DUPLICATE_FIELD,
281 ORDERFIELDS_ERROR_MISSING_FIELD,
282 ORDERFIELDS_ERROR_INVALID_ORDER_ARGUMENT,
283 ORDERFIELDS_ERROR_INTERNAL,
284];
285
286pub const ORDERFIELDS_DESCRIPTOR: BuiltinDescriptor = BuiltinDescriptor {
287 signatures: &ORDERFIELDS_SIGNATURES,
288 output_mode: BuiltinOutputMode::ByRequestedOutputCount,
289 completion_policy: BuiltinCompletionPolicy::Public,
290 errors: &ORDERFIELDS_ERRORS,
291};
292
293fn orderfields_error(error: &'static BuiltinErrorDescriptor) -> RuntimeError {
294 orderfields_error_with_message(error.message, error)
295}
296
297fn orderfields_error_with_message(
298 message: impl Into<String>,
299 error: &'static BuiltinErrorDescriptor,
300) -> RuntimeError {
301 let mut builder = build_runtime_error(message).with_builtin(BUILTIN_NAME);
302 if let Some(identifier) = error.identifier {
303 builder = builder.with_identifier(identifier);
304 }
305 builder.build()
306}
307
308#[runtime_builtin(
309 name = "orderfields",
310 category = "structs/core",
311 summary = "Reorder struct field definitions alphabetically or by supplied order.",
312 keywords = "orderfields,struct,reorder fields,alphabetical,struct array",
313 type_resolver(orderfields_type),
314 descriptor(crate::builtins::structs::core::orderfields::ORDERFIELDS_DESCRIPTOR),
315 builtin_path = "crate::builtins::structs::core::orderfields"
316)]
317async fn orderfields_builtin(value: Value, rest: Vec<Value>) -> BuiltinResult<Value> {
318 let eval = evaluate(value, &rest)?;
319 if let Some(out_count) = crate::output_count::current_output_count() {
320 if out_count == 0 {
321 return Ok(Value::OutputList(Vec::new()));
322 }
323 let (ordered, permutation) = eval.into_values();
324 let mut outputs = vec![ordered];
325 if out_count >= 2 {
326 outputs.push(permutation);
327 }
328 return Ok(crate::output_count::output_list_with_padding(
329 out_count, outputs,
330 ));
331 }
332 Ok(eval.into_ordered_value())
333}
334
335pub fn evaluate(value: Value, rest: &[Value]) -> BuiltinResult<OrderFieldsEvaluation> {
337 if rest.len() > 1 {
338 return Err(orderfields_error(&ORDERFIELDS_ERROR_TOO_MANY_INPUTS));
339 }
340 let order_arg = rest.first();
341
342 match value {
343 Value::Struct(struct_value) => {
344 let original: Vec<String> = struct_value.field_names().cloned().collect();
345 let order = resolve_order(&struct_value, order_arg)?;
346 let permutation = permutation_from(&original, &order)?;
347 let permutation = permutation_tensor(permutation)?;
348 let reordered = reorder_struct(&struct_value, &order)?;
349 Ok(OrderFieldsEvaluation::new(
350 Value::Struct(reordered),
351 permutation,
352 ))
353 }
354 Value::Cell(cell) => {
355 if cell.data.is_empty() {
356 let permutation = permutation_tensor(Vec::new())?;
357 if let Some(arg) = order_arg {
358 if let Some(reference) = extract_reference_struct(arg)? {
359 if reference.fields.is_empty() {
360 return Ok(OrderFieldsEvaluation::new(Value::Cell(cell), permutation));
361 } else {
362 return Err(orderfields_error(&ORDERFIELDS_ERROR_EMPTY_STRUCT_ARRAY));
363 }
364 }
365 if let Some(names) = extract_name_list(arg)? {
366 if names.is_empty() {
367 return Ok(OrderFieldsEvaluation::new(Value::Cell(cell), permutation));
368 }
369 return Err(orderfields_error(&ORDERFIELDS_ERROR_NO_FIELDS));
370 }
371 if let Value::Tensor(tensor) = arg {
372 if tensor.data.is_empty() {
373 return Ok(OrderFieldsEvaluation::new(Value::Cell(cell), permutation));
374 }
375 return Err(orderfields_error(&ORDERFIELDS_ERROR_NO_FIELDS));
376 }
377 return Err(orderfields_error(&ORDERFIELDS_ERROR_NO_FIELDS));
378 }
379 return Ok(OrderFieldsEvaluation::new(Value::Cell(cell), permutation));
380 }
381 let first = extract_struct_from_cell(&cell, 0)?;
382 let original: Vec<String> = first.field_names().cloned().collect();
383 let order = resolve_order(&first, order_arg)?;
384 let permutation = permutation_from(&original, &order)?;
385 let permutation = permutation_tensor(permutation)?;
386 let reordered = reorder_struct_array(&cell, &order)?;
387 Ok(OrderFieldsEvaluation::new(
388 Value::Cell(reordered),
389 permutation,
390 ))
391 }
392 other => Err(orderfields_error_with_message(
393 format!(
394 "{} (got {other:?})",
395 ORDERFIELDS_ERROR_INVALID_INPUT.message
396 ),
397 &ORDERFIELDS_ERROR_INVALID_INPUT,
398 )),
399 }
400}
401
402pub struct OrderFieldsEvaluation {
403 ordered: Value,
404 permutation: Tensor,
405}
406
407impl OrderFieldsEvaluation {
408 fn new(ordered: Value, permutation: Tensor) -> Self {
409 Self {
410 ordered,
411 permutation,
412 }
413 }
414
415 pub fn into_ordered_value(self) -> Value {
416 self.ordered
417 }
418
419 pub fn permutation_value(&self) -> Value {
420 tensor::tensor_into_value(self.permutation.clone())
421 }
422
423 pub fn into_values(self) -> (Value, Value) {
424 let perm = tensor::tensor_into_value(self.permutation);
425 (self.ordered, perm)
426 }
427}
428
429fn reorder_struct_array(array: &CellArray, order: &[String]) -> BuiltinResult<CellArray> {
430 let mut reordered_elems = Vec::with_capacity(array.data.len());
431 for (index, handle) in array.data.iter().enumerate() {
432 let value = unsafe { &*handle.as_raw() };
433 let Value::Struct(st) = value else {
434 return Err(orderfields_error_with_message(
435 format!(
436 "{} (element {} is not a struct)",
437 ORDERFIELDS_ERROR_INVALID_STRUCT_ARRAY.message,
438 index + 1
439 ),
440 &ORDERFIELDS_ERROR_INVALID_STRUCT_ARRAY,
441 ));
442 };
443 ensure_same_field_set(order, st)?;
444 let reordered = reorder_struct(st, order)?;
445 reordered_elems.push(Value::Struct(reordered));
446 }
447 CellArray::new_with_shape(reordered_elems, array.shape.clone()).map_err(|e| {
448 orderfields_error_with_message(
449 format!("{}: {e}", ORDERFIELDS_ERROR_REBUILD_FAILED.message),
450 &ORDERFIELDS_ERROR_REBUILD_FAILED,
451 )
452 })
453}
454
455fn reorder_struct(struct_value: &StructValue, order: &[String]) -> BuiltinResult<StructValue> {
456 let mut reordered = StructValue::new();
457 for name in order {
458 let value = struct_value
459 .fields
460 .get(name)
461 .ok_or_else(|| missing_field(name))?
462 .clone();
463 reordered.fields.insert(name.clone(), value);
464 }
465 Ok(reordered)
466}
467
468fn resolve_order(
469 struct_value: &StructValue,
470 order_arg: Option<&Value>,
471) -> BuiltinResult<Vec<String>> {
472 let mut current: Vec<String> = struct_value.field_names().cloned().collect();
473 if let Some(arg) = order_arg {
474 if let Some(reference) = extract_reference_struct(arg)? {
475 let reference_names: Vec<String> = reference.field_names().cloned().collect();
476 ensure_same_field_set(&reference_names, struct_value)?;
477 return Ok(reference_names);
478 }
479
480 if let Some(names) = extract_name_list(arg)? {
481 ensure_same_field_set(&names, struct_value)?;
482 return Ok(names);
483 }
484
485 if let Some(permutation) = extract_indices(¤t, arg)? {
486 return Ok(permutation);
487 }
488
489 return Err(orderfields_error(&ORDERFIELDS_ERROR_INVALID_ORDER_ARGUMENT));
490 }
491
492 sort_field_names(&mut current);
493 Ok(current)
494}
495
496fn permutation_from(original: &[String], order: &[String]) -> BuiltinResult<Vec<f64>> {
497 let mut index_map = HashMap::with_capacity(original.len());
498 for (idx, name) in original.iter().enumerate() {
499 index_map.insert(name.as_str(), idx);
500 }
501 let mut indices = Vec::with_capacity(order.len());
502 for name in order {
503 let Some(position) = index_map.get(name.as_str()) else {
504 return Err(missing_field(name));
505 };
506 indices.push((*position as f64) + 1.0);
507 }
508 Ok(indices)
509}
510
511fn permutation_tensor(indices: Vec<f64>) -> BuiltinResult<Tensor> {
512 let rows = indices.len();
513 let shape = vec![rows, 1];
514 Tensor::new(indices, shape).map_err(|e| {
515 orderfields_error_with_message(
516 format!("{}: {e}", ORDERFIELDS_ERROR_INTERNAL.message),
517 &ORDERFIELDS_ERROR_INTERNAL,
518 )
519 })
520}
521
522fn sort_field_names(names: &mut [String]) {
523 names.sort_by(|a, b| {
524 let lower_a = a.to_ascii_lowercase();
525 let lower_b = b.to_ascii_lowercase();
526 match lower_a.cmp(&lower_b) {
527 Ordering::Equal => a.cmp(b),
528 other => other,
529 }
530 });
531}
532
533fn extract_reference_struct(value: &Value) -> BuiltinResult<Option<StructValue>> {
534 match value {
535 Value::Struct(st) => Ok(Some(st.clone())),
536 Value::Cell(cell) => {
537 let mut first: Option<StructValue> = None;
538 for (index, handle) in cell.data.iter().enumerate() {
539 let value = unsafe { &*handle.as_raw() };
540 if let Value::Struct(st) = value {
541 if first.is_none() {
542 first = Some(st.clone());
543 }
544 } else if first.is_some() {
545 return Err(orderfields_error_with_message(
546 format!(
547 "{} (element {} is not a struct)",
548 ORDERFIELDS_ERROR_INVALID_REFERENCE.message,
549 index + 1
550 ),
551 &ORDERFIELDS_ERROR_INVALID_REFERENCE,
552 ));
553 } else {
554 return Ok(None);
555 }
556 }
557 Ok(first)
558 }
559 _ => Ok(None),
560 }
561}
562
563fn extract_name_list(arg: &Value) -> BuiltinResult<Option<Vec<String>>> {
564 match arg {
565 Value::Cell(cell) => {
566 let mut names = Vec::with_capacity(cell.data.len());
567 for (index, handle) in cell.data.iter().enumerate() {
568 let value = unsafe { &*handle.as_raw() };
569 let text = scalar_string(value).ok_or_else(|| {
570 orderfields_error_with_message(
571 format!(
572 "{} (cell array element {})",
573 ORDERFIELDS_ERROR_INVALID_NAME_LIST.message,
574 index + 1
575 ),
576 &ORDERFIELDS_ERROR_INVALID_NAME_LIST,
577 )
578 })?;
579 if text.is_empty() {
580 return Err(orderfields_error(&ORDERFIELDS_ERROR_EMPTY_FIELD_NAME));
581 }
582 names.push(text);
583 }
584 Ok(Some(names))
585 }
586 Value::StringArray(sa) => Ok(Some(sa.data.clone())),
587 Value::CharArray(ca) => {
588 if ca.rows == 0 {
589 return Ok(Some(Vec::new()));
590 }
591 let mut names = Vec::with_capacity(ca.rows);
592 for row in 0..ca.rows {
593 let start = row * ca.cols;
594 let end = start + ca.cols;
595 let mut text: String = ca.data[start..end].iter().collect();
596 while text.ends_with(' ') {
597 text.pop();
598 }
599 if text.is_empty() {
600 return Err(orderfields_error(&ORDERFIELDS_ERROR_EMPTY_FIELD_NAME));
601 }
602 names.push(text);
603 }
604 Ok(Some(names))
605 }
606 _ => Ok(None),
607 }
608}
609
610fn extract_indices(current: &[String], arg: &Value) -> BuiltinResult<Option<Vec<String>>> {
611 let Value::Tensor(tensor) = arg else {
612 return Ok(None);
613 };
614 if tensor.data.is_empty() && current.is_empty() {
615 return Ok(Some(Vec::new()));
616 }
617 if tensor.data.len() != current.len() {
618 return Err(orderfields_error(&ORDERFIELDS_ERROR_INVALID_PERMUTATION));
619 }
620 let mut seen = HashSet::with_capacity(current.len());
621 let mut order = Vec::with_capacity(current.len());
622 for value in &tensor.data {
623 if !value.is_finite() || value.fract() != 0.0 {
624 return Err(orderfields_error(&ORDERFIELDS_ERROR_INDEX_NOT_INTEGER));
625 }
626 let idx = *value as isize;
627 if idx < 1 || idx as usize > current.len() {
628 return Err(orderfields_error(&ORDERFIELDS_ERROR_INDEX_OUT_OF_RANGE));
629 }
630 let zero_based = (idx as usize) - 1;
631 if !seen.insert(zero_based) {
632 return Err(orderfields_error(&ORDERFIELDS_ERROR_INDEX_DUPLICATE));
633 }
634 order.push(current[zero_based].clone());
635 }
636 Ok(Some(order))
637}
638
639fn ensure_same_field_set(order: &[String], original: &StructValue) -> BuiltinResult<()> {
640 if order.len() != original.fields.len() {
641 return Err(orderfields_error(&ORDERFIELDS_ERROR_FIELD_MISMATCH));
642 }
643 let mut seen = HashSet::with_capacity(order.len());
644 let original_set: HashSet<&str> = original.field_names().map(|s| s.as_str()).collect();
645 for name in order {
646 if !original_set.contains(name.as_str()) {
647 return Err(orderfields_error_with_message(
648 format!(
649 "{} ('{name}' in requested order)",
650 ORDERFIELDS_ERROR_UNKNOWN_FIELD.message
651 ),
652 &ORDERFIELDS_ERROR_UNKNOWN_FIELD,
653 ));
654 }
655 if !seen.insert(name.as_str()) {
656 return Err(orderfields_error_with_message(
657 format!(
658 "{} ('{name}' in requested order)",
659 ORDERFIELDS_ERROR_DUPLICATE_FIELD.message
660 ),
661 &ORDERFIELDS_ERROR_DUPLICATE_FIELD,
662 ));
663 }
664 }
665 Ok(())
666}
667
668fn extract_struct_from_cell(cell: &CellArray, index: usize) -> BuiltinResult<StructValue> {
669 let value = unsafe { &*cell.data[index].as_raw() };
670 match value {
671 Value::Struct(st) => Ok(st.clone()),
672 other => Err(orderfields_error_with_message(
673 format!(
674 "{} (found {other:?})",
675 ORDERFIELDS_ERROR_INVALID_STRUCT_CONTENTS.message
676 ),
677 &ORDERFIELDS_ERROR_INVALID_STRUCT_CONTENTS,
678 )),
679 }
680}
681
682fn scalar_string(value: &Value) -> Option<String> {
683 match value {
684 Value::String(s) => Some(s.clone()),
685 Value::StringArray(sa) if sa.data.len() == 1 => Some(sa.data[0].clone()),
686 Value::CharArray(ca) if ca.rows == 1 => {
687 let mut text: String = ca.data.iter().collect();
688 while text.ends_with(' ') {
689 text.pop();
690 }
691 Some(text)
692 }
693 _ => None,
694 }
695}
696
697fn missing_field(name: &str) -> RuntimeError {
698 orderfields_error_with_message(
699 format!("{} ('{name}')", ORDERFIELDS_ERROR_MISSING_FIELD.message),
700 &ORDERFIELDS_ERROR_MISSING_FIELD,
701 )
702}
703
704#[cfg(test)]
705pub(crate) mod tests {
706 use super::*;
707 use futures::executor::block_on;
708 use runmat_builtins::{CellArray, CharArray, StringArray, Tensor};
709
710 fn run_orderfields(value: Value, rest: Vec<Value>) -> BuiltinResult<Value> {
711 block_on(super::orderfields_builtin(value, rest))
712 }
713
714 fn assert_error_identifier(error: RuntimeError, expected: &str) {
715 assert_eq!(error.identifier(), Some(expected));
716 }
717
718 fn field_order(struct_value: &StructValue) -> Vec<String> {
719 struct_value.field_names().cloned().collect()
720 }
721
722 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
723 #[test]
724 fn default_sorts_alphabetically() {
725 let mut st = StructValue::new();
726 st.fields.insert("beta".to_string(), Value::Num(2.0));
727 st.fields.insert("alpha".to_string(), Value::Num(1.0));
728 st.fields.insert("gamma".to_string(), Value::Num(3.0));
729
730 let result = run_orderfields(Value::Struct(st), Vec::new()).expect("orderfields");
731 let Value::Struct(sorted) = result else {
732 panic!("expected struct result");
733 };
734 assert_eq!(
735 field_order(&sorted),
736 vec!["alpha".to_string(), "beta".to_string(), "gamma".to_string()]
737 );
738 }
739
740 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
741 #[test]
742 fn reorder_with_cell_name_list() {
743 let mut st = StructValue::new();
744 st.fields.insert("a".to_string(), Value::Num(1.0));
745 st.fields.insert("b".to_string(), Value::Num(2.0));
746 st.fields.insert("c".to_string(), Value::Num(3.0));
747 let names = CellArray::new(
748 vec![Value::from("c"), Value::from("a"), Value::from("b")],
749 1,
750 3,
751 )
752 .expect("cell");
753
754 let reordered =
755 run_orderfields(Value::Struct(st), vec![Value::Cell(names)]).expect("orderfields");
756 let Value::Struct(result) = reordered else {
757 panic!("expected struct result");
758 };
759 assert_eq!(
760 field_order(&result),
761 vec!["c".to_string(), "a".to_string(), "b".to_string()]
762 );
763 }
764
765 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
766 #[test]
767 fn reorder_with_string_array_names() {
768 let mut st = StructValue::new();
769 st.fields.insert("alpha".to_string(), Value::Num(1.0));
770 st.fields.insert("beta".to_string(), Value::Num(2.0));
771 st.fields.insert("gamma".to_string(), Value::Num(3.0));
772
773 let strings = StringArray::new(
774 vec!["gamma".into(), "alpha".into(), "beta".into()],
775 vec![1, 3],
776 )
777 .expect("string array");
778
779 let result = run_orderfields(Value::Struct(st), vec![Value::StringArray(strings)])
780 .expect("orderfields");
781 let Value::Struct(sorted) = result else {
782 panic!("expected struct result");
783 };
784 assert_eq!(
785 field_order(&sorted),
786 vec!["gamma".to_string(), "alpha".to_string(), "beta".to_string()]
787 );
788 }
789
790 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
791 #[test]
792 fn reorder_with_char_array_names() {
793 let mut st = StructValue::new();
794 st.fields.insert("cat".to_string(), Value::Num(1.0));
795 st.fields.insert("ant".to_string(), Value::Num(2.0));
796 st.fields.insert("bat".to_string(), Value::Num(3.0));
797
798 let data = vec!['b', 'a', 't', 'c', 'a', 't', 'a', 'n', 't'];
799 let char_array = CharArray::new(data, 3, 3).expect("char array");
800
801 let result =
802 run_orderfields(Value::Struct(st), vec![Value::CharArray(char_array)]).expect("order");
803 let Value::Struct(sorted) = result else {
804 panic!("expected struct result");
805 };
806 assert_eq!(
807 field_order(&sorted),
808 vec!["bat".to_string(), "cat".to_string(), "ant".to_string()]
809 );
810 }
811
812 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
813 #[test]
814 fn reorder_with_reference_struct() {
815 let mut source = StructValue::new();
816 source.fields.insert("y".to_string(), Value::Num(2.0));
817 source.fields.insert("x".to_string(), Value::Num(1.0));
818
819 let mut reference = StructValue::new();
820 reference.fields.insert("x".to_string(), Value::Num(0.0));
821 reference.fields.insert("y".to_string(), Value::Num(0.0));
822
823 let result = run_orderfields(
824 Value::Struct(source),
825 vec![Value::Struct(reference.clone())],
826 )
827 .expect("orderfields");
828 let Value::Struct(reordered) = result else {
829 panic!("expected struct result");
830 };
831 assert_eq!(
832 field_order(&reordered),
833 vec!["x".to_string(), "y".to_string()]
834 );
835 }
836
837 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
838 #[test]
839 fn reorder_with_index_vector() {
840 let mut st = StructValue::new();
841 st.fields.insert("first".to_string(), Value::Num(1.0));
842 st.fields.insert("second".to_string(), Value::Num(2.0));
843 st.fields.insert("third".to_string(), Value::Num(3.0));
844
845 let permutation = Tensor::new(vec![3.0, 1.0, 2.0], vec![1, 3]).expect("tensor permutation");
846 let result = run_orderfields(Value::Struct(st), vec![Value::Tensor(permutation)])
847 .expect("orderfields");
848 let Value::Struct(reordered) = result else {
849 panic!("expected struct result");
850 };
851 assert_eq!(
852 field_order(&reordered),
853 vec![
854 "third".to_string(),
855 "first".to_string(),
856 "second".to_string()
857 ]
858 );
859 }
860
861 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
862 #[test]
863 fn index_vector_must_be_integers() {
864 let mut st = StructValue::new();
865 st.fields.insert("one".to_string(), Value::Num(1.0));
866 st.fields.insert("two".to_string(), Value::Num(2.0));
867
868 let permutation = Tensor::new(vec![1.0, 1.5], vec![1, 2]).expect("tensor");
869 let err = run_orderfields(Value::Struct(st), vec![Value::Tensor(permutation)]).unwrap_err();
870 assert_error_identifier(err, ORDERFIELDS_ERROR_INDEX_NOT_INTEGER.identifier.unwrap());
871 }
872
873 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
874 #[test]
875 fn permutation_vector_matches_original_positions() {
876 let mut st = StructValue::new();
877 st.fields.insert("beta".to_string(), Value::Num(2.0));
878 st.fields.insert("alpha".to_string(), Value::Num(1.0));
879 st.fields.insert("gamma".to_string(), Value::Num(3.0));
880
881 let eval = evaluate(Value::Struct(st), &[]).expect("evaluate");
882 let perm = eval.permutation_value();
883 match perm {
884 Value::Tensor(t) => assert_eq!(t.data, vec![2.0, 1.0, 3.0]),
885 other => panic!("expected tensor permutation, got {other:?}"),
886 }
887 let Value::Struct(ordered) = eval.into_ordered_value() else {
888 panic!("expected struct result");
889 };
890 assert_eq!(
891 field_order(&ordered),
892 vec!["alpha".to_string(), "beta".to_string(), "gamma".to_string()]
893 );
894 }
895
896 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
897 #[test]
898 fn reorder_struct_array() {
899 let mut first = StructValue::new();
900 first.fields.insert("b".to_string(), Value::Num(1.0));
901 first.fields.insert("a".to_string(), Value::Num(2.0));
902 let mut second = StructValue::new();
903 second.fields.insert("b".to_string(), Value::Num(3.0));
904 second.fields.insert("a".to_string(), Value::Num(4.0));
905 let array = CellArray::new_with_shape(
906 vec![Value::Struct(first), Value::Struct(second)],
907 vec![1, 2],
908 )
909 .expect("struct array");
910 let names =
911 CellArray::new(vec![Value::from("a"), Value::from("b")], 1, 2).expect("cell names");
912
913 let result =
914 run_orderfields(Value::Cell(array), vec![Value::Cell(names)]).expect("orderfields");
915 let Value::Cell(reordered) = result else {
916 panic!("expected cell array");
917 };
918 for handle in &reordered.data {
919 let Value::Struct(st) = (unsafe { &*handle.as_raw() }) else {
920 panic!("expected struct element");
921 };
922 assert_eq!(field_order(st), vec!["a".to_string(), "b".to_string()]);
923 }
924 }
925
926 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
927 #[test]
928 fn struct_array_permutation_reuses_order() {
929 let mut first = StructValue::new();
930 first.fields.insert("z".to_string(), Value::Num(1.0));
931 first.fields.insert("x".to_string(), Value::Num(2.0));
932 first.fields.insert("y".to_string(), Value::Num(3.0));
933
934 let mut second = StructValue::new();
935 second.fields.insert("z".to_string(), Value::Num(4.0));
936 second.fields.insert("x".to_string(), Value::Num(5.0));
937 second.fields.insert("y".to_string(), Value::Num(6.0));
938
939 let array = CellArray::new_with_shape(
940 vec![Value::Struct(first), Value::Struct(second)],
941 vec![1, 2],
942 )
943 .expect("struct array");
944
945 let eval = evaluate(Value::Cell(array), &[]).expect("evaluate");
946 let perm = eval.permutation_value();
947 match perm {
948 Value::Tensor(t) => assert_eq!(t.data, vec![2.0, 3.0, 1.0]),
949 other => panic!("expected tensor permutation, got {other:?}"),
950 }
951 }
952
953 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
954 #[test]
955 fn rejects_unknown_field() {
956 let mut st = StructValue::new();
957 st.fields.insert("alpha".to_string(), Value::Num(1.0));
958 st.fields.insert("beta".to_string(), Value::Num(2.0));
959 let err = run_orderfields(
960 Value::Struct(st),
961 vec![Value::Cell(
962 CellArray::new(vec![Value::from("beta"), Value::from("gamma")], 1, 2)
963 .expect("cell"),
964 )],
965 )
966 .unwrap_err();
967 assert_error_identifier(err, ORDERFIELDS_ERROR_UNKNOWN_FIELD.identifier.unwrap());
968 }
969
970 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
971 #[test]
972 fn duplicate_field_names_rejected() {
973 let mut st = StructValue::new();
974 st.fields.insert("alpha".to_string(), Value::Num(1.0));
975 st.fields.insert("beta".to_string(), Value::Num(2.0));
976
977 let names =
978 CellArray::new(vec![Value::from("alpha"), Value::from("alpha")], 1, 2).expect("cell");
979 let err = run_orderfields(Value::Struct(st), vec![Value::Cell(names)]).unwrap_err();
980 assert_error_identifier(err, ORDERFIELDS_ERROR_DUPLICATE_FIELD.identifier.unwrap());
981 }
982
983 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
984 #[test]
985 fn reference_struct_mismatch_errors() {
986 let mut source = StructValue::new();
987 source.fields.insert("x".to_string(), Value::Num(1.0));
988 source.fields.insert("y".to_string(), Value::Num(2.0));
989
990 let mut reference = StructValue::new();
991 reference.fields.insert("x".to_string(), Value::Num(0.0));
992
993 let err =
994 run_orderfields(Value::Struct(source), vec![Value::Struct(reference)]).unwrap_err();
995 assert_error_identifier(err, ORDERFIELDS_ERROR_FIELD_MISMATCH.identifier.unwrap());
996 }
997
998 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
999 #[test]
1000 fn invalid_order_argument_type_errors() {
1001 let mut st = StructValue::new();
1002 st.fields.insert("x".to_string(), Value::Num(1.0));
1003
1004 let err = run_orderfields(Value::Struct(st), vec![Value::Num(1.0)]).unwrap_err();
1005 assert_error_identifier(
1006 err,
1007 ORDERFIELDS_ERROR_INVALID_ORDER_ARGUMENT.identifier.unwrap(),
1008 );
1009 }
1010
1011 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1012 #[test]
1013 fn empty_struct_array_nonempty_reference_errors() {
1014 let empty = CellArray::new(Vec::new(), 0, 0).expect("empty struct array");
1015 let mut reference = StructValue::new();
1016 reference
1017 .fields
1018 .insert("field".to_string(), Value::Num(1.0));
1019
1020 let err = run_orderfields(Value::Cell(empty), vec![Value::Struct(reference)]).unwrap_err();
1021 assert_error_identifier(
1022 err,
1023 ORDERFIELDS_ERROR_EMPTY_STRUCT_ARRAY.identifier.unwrap(),
1024 );
1025 }
1026}