solverforge 0.6.12__tar.gz → 0.6.13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. {solverforge-0.6.12 → solverforge-0.6.13}/Cargo.lock +1 -1
  2. {solverforge-0.6.12 → solverforge-0.6.13}/Cargo.toml +1 -1
  3. {solverforge-0.6.12 → solverforge-0.6.13}/PKG-INFO +5 -5
  4. {solverforge-0.6.12 → solverforge-0.6.13}/README.md +4 -4
  5. {solverforge-0.6.12 → solverforge-0.6.13}/pyproject.toml +1 -1
  6. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/__init__.py +1 -1
  7. {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/dynamic_assignment_group.rs +102 -8
  8. {solverforge-0.6.12 → solverforge-0.6.13}/src/state/entity_table.rs +14 -0
  9. {solverforge-0.6.12 → solverforge-0.6.13}/src/state/marshal.rs +56 -1
  10. {solverforge-0.6.12 → solverforge-0.6.13}/LICENSE +0 -0
  11. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/_native.pyi +0 -0
  12. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/config.py +0 -0
  13. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/console.py +0 -0
  14. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/constraints.py +0 -0
  15. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/decorators.py +0 -0
  16. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/errors.py +0 -0
  17. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/events.py +0 -0
  18. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/fields.py +0 -0
  19. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/groups.py +0 -0
  20. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/joiner.py +0 -0
  21. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/manager.py +0 -0
  22. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/model.py +0 -0
  23. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/py.typed +0 -0
  24. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/score.py +0 -0
  25. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/solver.py +0 -0
  26. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/typing.py +0 -0
  27. {solverforge-0.6.12 → solverforge-0.6.13}/python/solverforge/ui.py +0 -0
  28. {solverforge-0.6.12 → solverforge-0.6.13}/rust-toolchain.toml +0 -0
  29. {solverforge-0.6.12 → solverforge-0.6.13}/src/bindings.rs +0 -0
  30. {solverforge-0.6.12 → solverforge-0.6.13}/src/callbacks/call.rs +0 -0
  31. {solverforge-0.6.12 → solverforge-0.6.13}/src/callbacks/mod.rs +0 -0
  32. {solverforge-0.6.12 → solverforge-0.6.13}/src/callbacks/registry.rs +0 -0
  33. {solverforge-0.6.12 → solverforge-0.6.13}/src/callbacks/threading.rs +0 -0
  34. {solverforge-0.6.12 → solverforge-0.6.13}/src/config.rs +0 -0
  35. {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/compiled.rs +0 -0
  36. {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/evaluate.rs +0 -0
  37. {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/incremental.rs +0 -0
  38. {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/list_precedence.rs +0 -0
  39. {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/matches.rs +0 -0
  40. {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/mod.rs +0 -0
  41. {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/state.rs +0 -0
  42. {solverforge-0.6.12 → solverforge-0.6.13}/src/constraints/stream_plan.rs +0 -0
  43. {solverforge-0.6.12 → solverforge-0.6.13}/src/descriptor/extractor.rs +0 -0
  44. {solverforge-0.6.12 → solverforge-0.6.13}/src/descriptor/mod.rs +0 -0
  45. {solverforge-0.6.12 → solverforge-0.6.13}/src/descriptor/variable.rs +0 -0
  46. {solverforge-0.6.12 → solverforge-0.6.13}/src/error.rs +0 -0
  47. {solverforge-0.6.12 → solverforge-0.6.13}/src/intern.rs +0 -0
  48. {solverforge-0.6.12 → solverforge-0.6.13}/src/lib.rs +0 -0
  49. {solverforge-0.6.12 → solverforge-0.6.13}/src/manager/candidate_trace.rs +0 -0
  50. {solverforge-0.6.12 → solverforge-0.6.13}/src/manager/events.rs +0 -0
  51. {solverforge-0.6.12 → solverforge-0.6.13}/src/manager/jobs.rs +0 -0
  52. {solverforge-0.6.12 → solverforge-0.6.13}/src/manager/mod.rs +0 -0
  53. {solverforge-0.6.12 → solverforge-0.6.13}/src/manager/provenance.rs +0 -0
  54. {solverforge-0.6.12 → solverforge-0.6.13}/src/proxy/entity.rs +0 -0
  55. {solverforge-0.6.12 → solverforge-0.6.13}/src/proxy/list.rs +0 -0
  56. {solverforge-0.6.12 → solverforge-0.6.13}/src/proxy/mod.rs +0 -0
  57. {solverforge-0.6.12 → solverforge-0.6.13}/src/proxy/solution.rs +0 -0
  58. {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/candidate_metric.rs +0 -0
  59. {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/dynamic_provider.rs +0 -0
  60. {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/list_slots.rs +0 -0
  61. {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/mod.rs +0 -0
  62. {solverforge-0.6.12 → solverforge-0.6.13}/src/runtime/scalar_slots.rs +0 -0
  63. {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/build.rs +0 -0
  64. {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/compiled.rs +0 -0
  65. {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/mod.rs +0 -0
  66. {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/parse.rs +0 -0
  67. {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/python.rs +0 -0
  68. {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/runtime_plan.rs +0 -0
  69. {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/types.rs +0 -0
  70. {solverforge-0.6.12 → solverforge-0.6.13}/src/schema/validate.rs +0 -0
  71. {solverforge-0.6.12 → solverforge-0.6.13}/src/score.rs +0 -0
  72. {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/api.rs +0 -0
  73. {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/console.rs +0 -0
  74. {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/mod.rs +0 -0
  75. {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/progress.rs +0 -0
  76. {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/result.rs +0 -0
  77. {solverforge-0.6.12 → solverforge-0.6.13}/src/solver/solvable.rs +0 -0
  78. {solverforge-0.6.12 → solverforge-0.6.13}/src/state/callback_view.rs +0 -0
  79. {solverforge-0.6.12 → solverforge-0.6.13}/src/state/clone.rs +0 -0
  80. {solverforge-0.6.12 → solverforge-0.6.13}/src/state/mod.rs +0 -0
  81. {solverforge-0.6.12 → solverforge-0.6.13}/src/state/solution.rs +0 -0
  82. {solverforge-0.6.12 → solverforge-0.6.13}/src/state/variables.rs +0 -0
  83. {solverforge-0.6.12 → solverforge-0.6.13}/src/ui.rs +0 -0
  84. {solverforge-0.6.12 → solverforge-0.6.13}/src/value.rs +0 -0
@@ -644,7 +644,7 @@ dependencies = [
644
644
 
645
645
  [[package]]
646
646
  name = "solverforge_py"
647
- version = "0.6.12"
647
+ version = "0.6.13"
648
648
  dependencies = [
649
649
  "indexmap",
650
650
  "once_cell",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "solverforge_py"
3
- version = "0.6.12"
3
+ version = "0.6.13"
4
4
  edition = "2021"
5
5
  license = "Apache-2.0"
6
6
  rust-version = "1.95"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: solverforge
3
- Version: 0.6.12
3
+ Version: 0.6.13
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Intended Audience :: Science/Research
@@ -51,12 +51,12 @@ requirements, and other APIs that are not part of this package.
51
51
 
52
52
  ## Installation
53
53
 
54
- The package version prepared by this checkout is `solverforge` `0.6.12`, targeting
54
+ The package version prepared by this checkout is `solverforge` `0.6.13`, targeting
55
55
  the published and registry-locked SolverForge Rust `0.21.2` dependency base.
56
56
  Install the matching package after its Python release with:
57
57
 
58
58
  ```sh
59
- python3.14 -m pip install "solverforge==0.6.12"
59
+ python3.14 -m pip install "solverforge==0.6.13"
60
60
  ```
61
61
 
62
62
  The installable wheel contains the core `solverforge` package, native extension,
@@ -76,12 +76,12 @@ make deliveries-run PORT=7861
76
76
  ```
77
77
 
78
78
  The same source-checkout examples can be run against the matching published
79
- package once `0.6.12` is released, without `PYTHONPATH` or an editable checkout:
79
+ package once `0.6.13` is released, without `PYTHONPATH` or an editable checkout:
80
80
 
81
81
  ```sh
82
82
  python3.14 -m venv .venv-examples
83
83
  . .venv-examples/bin/activate
84
- python -m pip install "solverforge[examples]==0.6.12"
84
+ python -m pip install "solverforge[examples]==0.6.13"
85
85
  python examples/nqueens.py
86
86
  python -m examples.solverforge_hospital
87
87
  python -m examples.solverforge_deliveries
@@ -20,12 +20,12 @@ requirements, and other APIs that are not part of this package.
20
20
 
21
21
  ## Installation
22
22
 
23
- The package version prepared by this checkout is `solverforge` `0.6.12`, targeting
23
+ The package version prepared by this checkout is `solverforge` `0.6.13`, targeting
24
24
  the published and registry-locked SolverForge Rust `0.21.2` dependency base.
25
25
  Install the matching package after its Python release with:
26
26
 
27
27
  ```sh
28
- python3.14 -m pip install "solverforge==0.6.12"
28
+ python3.14 -m pip install "solverforge==0.6.13"
29
29
  ```
30
30
 
31
31
  The installable wheel contains the core `solverforge` package, native extension,
@@ -45,12 +45,12 @@ make deliveries-run PORT=7861
45
45
  ```
46
46
 
47
47
  The same source-checkout examples can be run against the matching published
48
- package once `0.6.12` is released, without `PYTHONPATH` or an editable checkout:
48
+ package once `0.6.13` is released, without `PYTHONPATH` or an editable checkout:
49
49
 
50
50
  ```sh
51
51
  python3.14 -m venv .venv-examples
52
52
  . .venv-examples/bin/activate
53
- python -m pip install "solverforge[examples]==0.6.12"
53
+ python -m pip install "solverforge[examples]==0.6.13"
54
54
  python examples/nqueens.py
55
55
  python -m examples.solverforge_hospital
56
56
  python -m examples.solverforge_deliveries
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "solverforge"
7
- version = "0.6.12"
7
+ version = "0.6.13"
8
8
  description = "Dynamic Python bindings for the SolverForge constraint solver."
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
@@ -41,7 +41,7 @@ from .manager import JobHandle, SolverManager
41
41
  from .score import HardMediumSoftScore, HardSoftDecimalScore, HardSoftScore, SoftScore
42
42
  from .solver import Solver
43
43
 
44
- __version__ = "0.6.12"
44
+ __version__ = "0.6.13"
45
45
 
46
46
  __all__ = [
47
47
  "CallbackError",
@@ -123,7 +123,6 @@ fn build_assignment_group(
123
123
  let metadata = Arc::new(PyDynamicAssignmentMetadata {
124
124
  schema,
125
125
  group_index,
126
- entity_index,
127
126
  });
128
127
  Ok(ScalarGroupBinding::dynamic_assignment(
129
128
  intern(group.name.clone()),
@@ -146,7 +145,6 @@ fn scalar_group_limits(group: &AssignmentScalarGroupSchema) -> ScalarGroupLimits
146
145
  struct PyDynamicAssignmentMetadata {
147
146
  schema: Arc<DynamicSchema>,
148
147
  group_index: usize,
149
- entity_index: usize,
150
148
  }
151
149
 
152
150
  impl PyDynamicAssignmentMetadata {
@@ -182,10 +180,16 @@ impl PyDynamicAssignmentMetadata {
182
180
  };
183
181
  solution
184
182
  .state
185
- .entities
186
- .get(self.entity_index)
183
+ .assignment_group_fields
184
+ .get(self.group_index)
187
185
  .and_then(|rows| rows.get(entity_index))
188
- .and_then(|row| row.fields.get(field_name))
186
+ .and_then(|fields| match hook_name {
187
+ "required_entity" => fields.required_entity.as_ref(),
188
+ "capacity_key" => fields.capacity_key.as_ref(),
189
+ "position_key" => fields.position_key.as_ref(),
190
+ "sequence_key" => fields.sequence_key.as_ref(),
191
+ _ => unreachable!("assignment metadata source resolved"),
192
+ })
189
193
  .map(Some)
190
194
  .ok_or_else(|| {
191
195
  py_err(format!(
@@ -420,10 +424,10 @@ impl PyDynamicAssignmentMetadata {
420
424
  ) -> PyResult<bool> {
421
425
  let value = solution
422
426
  .state
423
- .entities
424
- .get(self.entity_index)
427
+ .assignment_group_fields
428
+ .get(self.group_index)
425
429
  .and_then(|rows| rows.get(entity_index))
426
- .and_then(|row| row.fields.get(field_name))
430
+ .and_then(|fields| fields.same_value_conflicts.as_ref())
427
431
  .ok_or_else(|| {
428
432
  py_err(format!(
429
433
  "assignment scalar group `{}` field `{field_name}` is missing on row {entity_index}",
@@ -547,3 +551,93 @@ fn indexed_usize(field_value: &DynamicValue, candidate_value: usize) -> PyResult
547
551
  )),
548
552
  }
549
553
  }
554
+
555
+ #[cfg(test)]
556
+ mod tests {
557
+ use pyo3::ffi::c_str;
558
+
559
+ use super::*;
560
+ use crate::schema::parse::parse_schema;
561
+ use crate::schema::runtime_plan::CompiledRuntimePlan;
562
+ use crate::state::callback_view::PythonCallbackView;
563
+ use crate::state::entity_table::{AssignmentRowFields, DynamicEntityRow, DynamicState};
564
+
565
+ #[test]
566
+ fn assignment_access_uses_imported_slots_not_named_row_fields() {
567
+ Python::initialize();
568
+ Python::attach(|py| {
569
+ let raw = py
570
+ .eval(
571
+ c_str!(
572
+ r#"{
573
+ 'solution_type': 'Plan', 'score_family': 'hard_soft',
574
+ 'entities': [{'type_name': 'Task', 'collection': 'tasks', 'fields': [
575
+ {'name': 'value', 'kind': 'planning_variable', 'allows_unassigned': True}
576
+ ]}],
577
+ 'constraints': [], 'conflict_repairs': [], 'candidate_metrics': [],
578
+ 'scalar_groups': [{'kind': 'assignment', 'name': 'assign',
579
+ 'entity_class': 'Task', 'variable_name': 'value',
580
+ 'required_entity_field': 'required', 'capacity_key_field': 'capacity',
581
+ 'position_key_field': 'position', 'sequence_key_field': 'sequence',
582
+ 'same_value_conflict_field': 'conflicts'}]
583
+ }"#
584
+ ),
585
+ None,
586
+ None,
587
+ )
588
+ .unwrap();
589
+ let schema = Arc::new(parse_schema(raw.cast().unwrap()).unwrap());
590
+ let plan = Arc::new(CompiledRuntimePlan::from_schema(Arc::clone(&schema)).unwrap());
591
+ let metadata = PyDynamicAssignmentMetadata {
592
+ schema,
593
+ group_index: 0,
594
+ };
595
+ let state = DynamicState {
596
+ entities: vec![vec![DynamicEntityRow::default(); 2]],
597
+ assignment_group_fields: Arc::new(vec![vec![
598
+ AssignmentRowFields {
599
+ required_entity: Some(DynamicValue::Bool(true)),
600
+ capacity_key: Some(DynamicValue::List(vec![
601
+ DynamicValue::Int(7),
602
+ DynamicValue::None,
603
+ ])),
604
+ position_key: Some(DynamicValue::Int(13)),
605
+ sequence_key: Some(DynamicValue::Int(5)),
606
+ same_value_conflicts: Some(DynamicValue::List(vec![DynamicValue::Int(1)])),
607
+ },
608
+ AssignmentRowFields {
609
+ required_entity: Some(DynamicValue::Bool(false)),
610
+ same_value_conflicts: Some(DynamicValue::List(Vec::new())),
611
+ ..AssignmentRowFields::default()
612
+ },
613
+ ]]),
614
+ ..DynamicState::default()
615
+ };
616
+ let solution = PyDynamicSolution::from_runtime_plan(
617
+ plan,
618
+ state,
619
+ PythonCallbackView::default(),
620
+ None,
621
+ SolverConfig::default(),
622
+ 0,
623
+ );
624
+ let cloned = solution.clone();
625
+ assert!(Arc::ptr_eq(
626
+ &solution.state.assignment_group_fields,
627
+ &cloned.state.assignment_group_fields
628
+ ));
629
+ for state in [&solution, &cloned] {
630
+ assert!(state.state.entities[0][0].fields.is_empty());
631
+ assert!(metadata.required_entity(state, 0));
632
+ assert!(!metadata.required_entity(state, 1));
633
+ assert_eq!(metadata.capacity_key(state, 0, 0), Some(7));
634
+ assert_eq!(metadata.capacity_key(state, 0, 1), None);
635
+ assert_eq!(metadata.position_key(state, 0), Some(13));
636
+ assert_eq!(metadata.sequence_key(state, 0, 0), Some(5));
637
+ assert!(!metadata.assignment_edge_allowed(state, 0, 0, 1, 0));
638
+ assert!(!metadata.assignment_edge_allowed(state, 1, 0, 0, 0));
639
+ assert!(metadata.assignment_edge_allowed(state, 0, 0, 1, 1));
640
+ }
641
+ });
642
+ }
643
+ }
@@ -3,6 +3,19 @@ use std::sync::Arc;
3
3
 
4
4
  use crate::value::DynamicValue;
5
5
 
6
+ /// Immutable assignment row sources, resolved by group and hook once at import.
7
+ /// State clones share these values; planning-variable changes never rewrite them.
8
+ pub type AssignmentGroupFields = Vec<Vec<AssignmentRowFields>>;
9
+
10
+ #[derive(Debug, Clone, Default)]
11
+ pub struct AssignmentRowFields {
12
+ pub required_entity: Option<DynamicValue>,
13
+ pub capacity_key: Option<DynamicValue>,
14
+ pub position_key: Option<DynamicValue>,
15
+ pub sequence_key: Option<DynamicValue>,
16
+ pub same_value_conflicts: Option<DynamicValue>,
17
+ }
18
+
6
19
  #[derive(Debug, Clone, Default)]
7
20
  pub struct DynamicState {
8
21
  pub entities: Vec<Vec<DynamicEntityRow>>,
@@ -10,6 +23,7 @@ pub struct DynamicState {
10
23
  pub scalar_value_ranges: Vec<Vec<Option<Arc<[usize]>>>>,
11
24
  pub list_elements: Vec<Vec<Option<Arc<[usize]>>>>,
12
25
  pub solution_fields: Arc<BTreeMap<String, DynamicValue>>,
26
+ pub assignment_group_fields: Arc<AssignmentGroupFields>,
13
27
  }
14
28
 
15
29
  impl DynamicState {
@@ -12,7 +12,9 @@ use crate::schema::types::{
12
12
  };
13
13
  use crate::schema::DynamicSchema;
14
14
  use crate::state::callback_view::PythonCallbackView;
15
- use crate::state::entity_table::{DynamicEntityRow, DynamicState};
15
+ use crate::state::entity_table::{
16
+ AssignmentGroupFields, AssignmentRowFields, DynamicEntityRow, DynamicState,
17
+ };
16
18
  use crate::state::PyDynamicSolution;
17
19
  use crate::value::DynamicValue;
18
20
 
@@ -148,6 +150,7 @@ pub fn import_solution(
148
150
  fact_objects.push(objects);
149
151
  }
150
152
  validate_assignment_group_field_metadata(schema, &entity_tables)?;
153
+ let assignment_group_fields = resolve_assignment_group_fields(schema, &entity_tables)?;
151
154
  validate_list_element_metadata_field_sources(
152
155
  schema,
153
156
  &entity_tables,
@@ -163,6 +166,7 @@ pub fn import_solution(
163
166
  scalar_value_ranges,
164
167
  list_elements,
165
168
  solution_fields: Arc::new(solution_fields),
169
+ assignment_group_fields: Arc::new(assignment_group_fields),
166
170
  },
167
171
  PythonCallbackView::from_import(
168
172
  py_solution.clone().unbind(),
@@ -484,6 +488,57 @@ fn collect_list_metadata_row_fields(
484
488
  }
485
489
  }
486
490
 
491
+ fn resolve_assignment_group_fields(
492
+ schema: &DynamicSchema,
493
+ entity_tables: &[Vec<DynamicEntityRow>],
494
+ ) -> PyResult<AssignmentGroupFields> {
495
+ schema
496
+ .assignment_scalar_groups
497
+ .iter()
498
+ .map(|group| {
499
+ let entity_index = schema
500
+ .entity_index_by_type(&group.entity_class)
501
+ .expect("assignment group entity validated");
502
+ let fields = [
503
+ group
504
+ .required_entity
505
+ .as_ref()
506
+ .and_then(MetadataSourceSchema::row),
507
+ group
508
+ .capacity_key
509
+ .as_ref()
510
+ .and_then(MetadataSourceSchema::row),
511
+ group
512
+ .position_key
513
+ .as_ref()
514
+ .and_then(MetadataSourceSchema::row),
515
+ group
516
+ .sequence_key
517
+ .as_ref()
518
+ .and_then(MetadataSourceSchema::row),
519
+ group.same_value_conflict_field.as_deref(),
520
+ ];
521
+ entity_tables[entity_index]
522
+ .iter()
523
+ .enumerate()
524
+ .map(|(row_index, row)| {
525
+ let mut resolved = std::array::from_fn(|_| None);
526
+ for (index, field) in fields.iter().enumerate() {
527
+ if let Some(field) = field {
528
+ resolved[index] = Some(
529
+ assignment_metadata_field(row, &group.name, row_index, field)?
530
+ .clone(),
531
+ );
532
+ }
533
+ }
534
+ let [required_entity, capacity_key, position_key, sequence_key, same_value_conflicts] = resolved;
535
+ Ok(AssignmentRowFields { required_entity, capacity_key, position_key, sequence_key, same_value_conflicts })
536
+ })
537
+ .collect()
538
+ })
539
+ .collect()
540
+ }
541
+
487
542
  fn validate_assignment_group_field_metadata(
488
543
  schema: &DynamicSchema,
489
544
  entity_tables: &[Vec<DynamicEntityRow>],
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes