Skip to main content

solverforge_solver/descriptor_standard/
bindings.rs

1use std::any::Any;
2use std::fmt::{self, Debug};
3
4use solverforge_core::domain::{
5    PlanningSolution, SolutionDescriptor, UsizeEntityValueProvider, UsizeGetter, UsizeSetter,
6    ValueRangeType,
7};
8
9#[derive(Clone)]
10pub(crate) struct VariableBinding {
11    pub(crate) descriptor_index: usize,
12    pub(crate) entity_type_name: &'static str,
13    pub(crate) variable_name: &'static str,
14    pub(crate) getter: UsizeGetter,
15    pub(crate) setter: UsizeSetter,
16    pub(crate) value_range_provider: Option<&'static str>,
17    pub(crate) provider: Option<UsizeEntityValueProvider>,
18    pub(crate) range_type: ValueRangeType,
19}
20
21impl Debug for VariableBinding {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        f.debug_struct("VariableBinding")
24            .field("descriptor_index", &self.descriptor_index)
25            .field("entity_type_name", &self.entity_type_name)
26            .field("variable_name", &self.variable_name)
27            .field("range_type", &self.range_type)
28            .finish()
29    }
30}
31
32impl VariableBinding {
33    pub(crate) fn values_for_entity(
34        &self,
35        solution_descriptor: &SolutionDescriptor,
36        solution: &dyn Any,
37        entity: &dyn Any,
38    ) -> Vec<usize> {
39        match (&self.provider, &self.range_type) {
40            (Some(provider), _) => provider(entity),
41            (_, ValueRangeType::CountableRange { from, to }) => {
42                let start = *from;
43                let end = *to;
44                (start..end)
45                    .filter_map(|value| usize::try_from(value).ok())
46                    .collect()
47            }
48            _ => self
49                .value_range_provider
50                .and_then(|provider_name| {
51                    solution_descriptor
52                        .problem_fact_descriptors
53                        .iter()
54                        .find(|descriptor| descriptor.solution_field == provider_name)
55                        .and_then(|descriptor| descriptor.extractor.as_ref())
56                        .and_then(|extractor| extractor.count(solution))
57                        .or_else(|| {
58                            solution_descriptor
59                                .entity_descriptors
60                                .iter()
61                                .find(|descriptor| descriptor.solution_field == provider_name)
62                                .and_then(|descriptor| descriptor.extractor.as_ref())
63                                .and_then(|extractor| extractor.count(solution))
64                        })
65                })
66                .map(|count| (0..count).collect())
67                .unwrap_or_default(),
68        }
69    }
70}
71
72pub(crate) fn collect_bindings(descriptor: &SolutionDescriptor) -> Vec<VariableBinding> {
73    let mut bindings = Vec::new();
74    for (descriptor_index, entity_descriptor) in descriptor.entity_descriptors.iter().enumerate() {
75        for variable in entity_descriptor.genuine_variable_descriptors() {
76            let Some(getter) = variable.usize_getter else {
77                continue;
78            };
79            let Some(setter) = variable.usize_setter else {
80                continue;
81            };
82            bindings.push(VariableBinding {
83                descriptor_index,
84                entity_type_name: entity_descriptor.type_name,
85                variable_name: variable.name,
86                getter,
87                setter,
88                value_range_provider: variable.value_range_provider,
89                provider: variable.entity_value_provider,
90                range_type: variable.value_range_type.clone(),
91            });
92        }
93    }
94    bindings
95}
96
97pub(crate) fn find_binding(
98    bindings: &[VariableBinding],
99    entity_class: Option<&str>,
100    variable_name: Option<&str>,
101) -> Vec<VariableBinding> {
102    bindings
103        .iter()
104        .filter(|binding| entity_class.is_none_or(|name| name == binding.entity_type_name))
105        .filter(|binding| variable_name.is_none_or(|name| name == binding.variable_name))
106        .cloned()
107        .collect()
108}
109
110pub fn descriptor_has_bindings(descriptor: &SolutionDescriptor) -> bool {
111    !collect_bindings(descriptor).is_empty()
112}
113
114pub fn standard_work_remaining<S>(
115    descriptor: &SolutionDescriptor,
116    entity_class: Option<&str>,
117    variable_name: Option<&str>,
118    solution: &S,
119) -> bool
120where
121    S: PlanningSolution + 'static,
122{
123    let bindings = find_binding(&collect_bindings(descriptor), entity_class, variable_name);
124    for binding in bindings {
125        let Some(entity_count) = descriptor
126            .entity_descriptors
127            .get(binding.descriptor_index)
128            .and_then(|entity| entity.entity_count(solution as &dyn Any))
129        else {
130            continue;
131        };
132        for entity_index in 0..entity_count {
133            let entity = descriptor
134                .get_entity(solution as &dyn Any, binding.descriptor_index, entity_index)
135                .expect("entity lookup failed while checking standard work");
136            if (binding.getter)(entity).is_none()
137                && !binding
138                    .values_for_entity(descriptor, solution as &dyn Any, entity)
139                    .is_empty()
140            {
141                return true;
142            }
143        }
144    }
145    false
146}
147
148pub fn standard_target_matches(
149    descriptor: &SolutionDescriptor,
150    entity_class: Option<&str>,
151    variable_name: Option<&str>,
152) -> bool {
153    !find_binding(&collect_bindings(descriptor), entity_class, variable_name).is_empty()
154}