1use std::fmt;
9use std::sync::Arc;
10
11use solverforge_core::domain::{DynamicScalarVariableSlot, EntityClassId, VariableId};
12
13use super::{ScalarVariableSlot, ValueSource};
14
15#[derive(Clone, Debug, PartialEq, Eq, Hash)]
18pub struct RuntimeScalarSlotId {
19 pub descriptor_index: usize,
20 pub variable_index: usize,
21 pub entity_class: Arc<str>,
22 pub variable_name: Arc<str>,
23 pub dynamic_identity: Option<(EntityClassId, VariableId)>,
24}
25
26impl RuntimeScalarSlotId {
27 pub fn from_static_slot<S>(slot: &ScalarVariableSlot<S>) -> Self {
28 Self {
29 descriptor_index: slot.descriptor_index,
30 variable_index: slot.variable_index,
31 entity_class: Arc::from(slot.entity_type_name),
32 variable_name: Arc::from(slot.variable_name),
33 dynamic_identity: None,
34 }
35 }
36
37 pub fn from_dynamic_slot<S>(slot: &DynamicScalarVariableSlot<S>) -> Self {
38 Self {
39 descriptor_index: slot.descriptor_index(),
40 variable_index: slot.descriptor_variable_index(),
41 entity_class: Arc::from(slot.entity_type_name),
42 variable_name: Arc::from(slot.variable_name),
43 dynamic_identity: Some((slot.entity, slot.variable)),
44 }
45 }
46}
47
48impl fmt::Display for RuntimeScalarSlotId {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 write!(
51 f,
52 "{}.{} (descriptor {}, variable {})",
53 self.entity_class, self.variable_name, self.descriptor_index, self.variable_index
54 )
55 }
56}
57
58#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61pub enum ScalarAccessCapability {
62 Candidates,
63 NearbyValue,
64 NearbyEntity,
65 ConstructionEntityOrder,
66 ConstructionValueOrder,
67}
68
69pub enum RuntimeScalarSlot<S> {
72 Static(ScalarVariableSlot<S>),
73 Dynamic(DynamicScalarVariableSlot<S>),
74}
75
76impl<S> Clone for RuntimeScalarSlot<S> {
77 fn clone(&self) -> Self {
78 match self {
79 Self::Static(slot) => Self::Static(*slot),
80 Self::Dynamic(slot) => Self::Dynamic(slot.clone()),
81 }
82 }
83}
84
85impl<S> fmt::Debug for RuntimeScalarSlot<S> {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 f.debug_struct("RuntimeScalarSlot")
88 .field("id", &self.id())
89 .field("dynamic", &self.is_dynamic())
90 .finish()
91 }
92}
93
94impl<S> RuntimeScalarSlot<S> {
95 pub fn id(&self) -> RuntimeScalarSlotId {
96 match self {
97 Self::Static(slot) => RuntimeScalarSlotId::from_static_slot(slot),
98 Self::Dynamic(slot) => RuntimeScalarSlotId::from_dynamic_slot(slot),
99 }
100 }
101
102 pub fn matches_target(&self, entity_class: Option<&str>, variable_name: Option<&str>) -> bool {
103 match self {
104 Self::Static(slot) => slot.matches_target(entity_class, variable_name),
105 Self::Dynamic(slot) => slot.matches_target(entity_class, variable_name),
106 }
107 }
108
109 pub fn has_capability(&self, capability: ScalarAccessCapability) -> bool {
110 match (self, capability) {
111 (_, ScalarAccessCapability::Candidates) => true,
114 (Self::Static(slot), ScalarAccessCapability::NearbyValue) => {
115 slot.nearby_value_candidates.is_some()
116 }
117 (Self::Static(slot), ScalarAccessCapability::NearbyEntity) => {
118 slot.nearby_entity_candidates.is_some()
119 }
120 (Self::Static(slot), ScalarAccessCapability::ConstructionEntityOrder) => {
121 slot.construction_entity_order_key.is_some()
122 }
123 (Self::Static(slot), ScalarAccessCapability::ConstructionValueOrder) => {
124 slot.construction_value_order_key.is_some()
125 }
126 (Self::Dynamic(slot), ScalarAccessCapability::NearbyValue) => {
127 slot.has_nearby_value_candidates()
128 }
129 (Self::Dynamic(slot), ScalarAccessCapability::NearbyEntity) => {
130 slot.has_nearby_entity_candidates()
131 }
132 (Self::Dynamic(_), ScalarAccessCapability::ConstructionEntityOrder)
136 | (Self::Dynamic(_), ScalarAccessCapability::ConstructionValueOrder) => false,
137 }
138 }
139
140 pub(crate) fn descriptor_index(&self) -> usize {
143 match self {
144 Self::Static(slot) => slot.descriptor_index,
145 Self::Dynamic(slot) => slot.descriptor_index(),
146 }
147 }
148
149 pub(crate) fn variable_index(&self) -> usize {
152 match self {
153 Self::Static(slot) => slot.variable_index,
154 Self::Dynamic(slot) => slot.descriptor_variable_index(),
155 }
156 }
157
158 pub(crate) fn variable_name(&self) -> &'static str {
162 match self {
163 Self::Static(slot) => slot.variable_name,
164 Self::Dynamic(slot) => slot.variable_name,
165 }
166 }
167
168 pub(crate) fn entity_count(&self, solution: &S) -> usize {
169 match self {
170 Self::Static(slot) => (slot.entity_count)(solution),
171 Self::Dynamic(slot) => slot.entity_count(solution),
172 }
173 }
174
175 pub(crate) fn allows_unassigned(&self) -> bool {
179 match self {
180 Self::Static(slot) => slot.allows_unassigned,
181 Self::Dynamic(slot) => slot.allows_unassigned,
182 }
183 }
184
185 pub(crate) fn current_value(&self, solution: &S, entity_index: usize) -> Option<usize> {
186 match self {
187 Self::Static(slot) => slot.current_value(solution, entity_index),
188 Self::Dynamic(slot) => slot.current_value(solution, entity_index),
189 }
190 }
191
192 pub(crate) fn set_value(&self, solution: &mut S, entity_index: usize, value: Option<usize>) {
193 match self {
194 Self::Static(slot) => slot.set_value(solution, entity_index, value),
195 Self::Dynamic(slot) => slot.set_value(solution, entity_index, value),
196 }
197 }
198
199 pub(crate) fn value_is_legal(
200 &self,
201 solution: &S,
202 entity_index: usize,
203 value: Option<usize>,
204 ) -> bool {
205 match self {
206 Self::Static(slot) => slot.value_is_legal(solution, entity_index, value),
207 Self::Dynamic(slot) => slot.value_is_legal(solution, entity_index, value),
208 }
209 }
210
211 pub(crate) fn swap_destination_is_legal(
215 &self,
216 solution: &S,
217 entity_index: usize,
218 value: Option<usize>,
219 ) -> bool {
220 match self {
221 Self::Static(slot) if matches!(slot.value_source, ValueSource::Empty) => {
222 value.is_some()
223 }
224 Self::Static(slot) => slot.value_is_legal(solution, entity_index, value),
225 Self::Dynamic(slot) => slot.value_is_legal(solution, entity_index, value),
226 }
227 }
228
229 pub(crate) fn visit_candidate_values(
233 &self,
234 solution: &S,
235 entity_index: usize,
236 limit: Option<usize>,
237 visit: &mut dyn FnMut(usize),
238 ) {
239 let limit = limit.unwrap_or(usize::MAX);
240 match self {
241 Self::Static(slot) => {
242 for value in slot.candidate_values_for_entity(solution, entity_index, Some(limit)) {
243 visit(value);
244 }
245 }
246 Self::Dynamic(slot) => {
247 for &value in slot
248 .candidate_values(solution, entity_index)
249 .iter()
250 .take(limit)
251 {
252 visit(value);
253 }
254 }
255 }
256 }
257
258 pub(crate) fn visit_nearby_value_candidates(
262 &self,
263 solution: &S,
264 entity_index: usize,
265 limit: usize,
266 visit: &mut dyn FnMut(usize),
267 ) -> bool {
268 match self {
269 Self::Static(slot) => {
270 let Some(source) = slot.nearby_value_candidates else {
271 return false;
272 };
273 for &value in source(solution, entity_index, slot.variable_index)
274 .iter()
275 .take(limit)
276 {
277 visit(value);
278 }
279 true
280 }
281 Self::Dynamic(slot) => {
282 slot.visit_nearby_value_candidates(solution, entity_index, limit, visit)
283 }
284 }
285 }
286
287 pub(crate) fn nearby_value_distance(
288 &self,
289 solution: &S,
290 entity_index: usize,
291 candidate: usize,
292 ) -> Option<f64> {
293 match self {
294 Self::Static(slot) => slot.nearby_value_distance(solution, entity_index, candidate),
295 Self::Dynamic(slot) => slot.nearby_value_distance(solution, entity_index, candidate),
296 }
297 }
298
299 pub(crate) fn visit_nearby_entity_candidates(
300 &self,
301 solution: &S,
302 entity_index: usize,
303 limit: usize,
304 visit: &mut dyn FnMut(usize),
305 ) -> bool {
306 match self {
307 Self::Static(slot) => {
308 let Some(source) = slot.nearby_entity_candidates else {
309 return false;
310 };
311 for &candidate in source(solution, entity_index, slot.variable_index)
312 .iter()
313 .take(limit)
314 {
315 visit(candidate);
316 }
317 true
318 }
319 Self::Dynamic(slot) => {
320 slot.visit_nearby_entity_candidates(solution, entity_index, limit, visit)
321 }
322 }
323 }
324
325 pub(crate) fn nearby_entity_distance(
326 &self,
327 solution: &S,
328 left_entity_index: usize,
329 right_entity_index: usize,
330 ) -> Option<f64> {
331 match self {
332 Self::Static(slot) => {
333 slot.nearby_entity_distance(solution, left_entity_index, right_entity_index)
334 }
335 Self::Dynamic(slot) => {
336 slot.nearby_entity_distance(solution, left_entity_index, right_entity_index)
337 }
338 }
339 }
340
341 pub(crate) fn construction_entity_order_key(
342 &self,
343 solution: &S,
344 entity_index: usize,
345 ) -> Option<i64> {
346 match self {
347 Self::Static(slot) => slot.construction_entity_order_key(solution, entity_index),
348 Self::Dynamic(_) => None,
349 }
350 }
351
352 pub(crate) fn construction_value_order_key(
353 &self,
354 solution: &S,
355 entity_index: usize,
356 value: usize,
357 ) -> Option<i64> {
358 match self {
359 Self::Static(slot) => slot.construction_value_order_key(solution, entity_index, value),
360 Self::Dynamic(_) => None,
361 }
362 }
363
364 pub(crate) fn edit(
365 &self,
366 entity_index: usize,
367 to_value: Option<usize>,
368 ) -> RuntimeScalarEdit<S> {
369 RuntimeScalarEdit {
370 slot: self.clone(),
371 entity_index,
372 to_value,
373 }
374 }
375
376 pub fn is_dynamic(&self) -> bool {
377 matches!(self, Self::Dynamic(_))
378 }
379}
380
381pub struct RuntimeScalarEdit<S> {
386 pub slot: RuntimeScalarSlot<S>,
387 pub entity_index: usize,
388 pub to_value: Option<usize>,
389}
390
391impl<S> Clone for RuntimeScalarEdit<S> {
392 fn clone(&self) -> Self {
393 Self {
394 slot: self.slot.clone(),
395 entity_index: self.entity_index,
396 to_value: self.to_value,
397 }
398 }
399}
400
401impl<S> fmt::Debug for RuntimeScalarEdit<S> {
402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403 f.debug_struct("RuntimeScalarEdit")
404 .field("slot", &self.slot)
405 .field("entity_index", &self.entity_index)
406 .field("to_value", &self.to_value)
407 .finish()
408 }
409}
410
411impl<S> RuntimeScalarEdit<S> {
412 pub fn id(&self) -> RuntimeScalarSlotId {
413 self.slot.id()
414 }
415
416 pub(crate) fn descriptor_index(&self) -> usize {
417 self.slot.descriptor_index()
418 }
419
420 pub(crate) fn variable_index(&self) -> usize {
421 self.slot.variable_index()
422 }
423
424 pub(crate) fn variable_name(&self) -> &'static str {
425 self.slot.variable_name()
426 }
427}