solverforge_solver/builder/context/provider/
registry.rs1use std::collections::HashSet;
2use std::fmt;
3use std::sync::Arc;
4
5use super::super::{
6 ConflictRepair, RuntimeScalarSlot, RuntimeScalarSlotId, ScalarGroupBinding,
7 ScalarGroupBindingKind,
8};
9use super::native::{pull_static_group, pull_static_repair};
10use super::types::PanicProviderErrorBoundary;
11use super::{
12 ProviderNormalizationState, ProviderReasonArena, RawProviderCandidate,
13 ResolvedProviderCandidate, RuntimeConflictRepairProviderBinding,
14 RuntimeHostProviderErrorBoundary, RuntimeProviderHandle, RuntimeProviderLimits,
15 RuntimeProviderSlotResolver, RuntimeScalarGroupProviderBinding,
16 StaticConflictRepairProviderBinding, StaticScalarGroupProviderBinding,
17};
18use crate::{RepairCandidate, RepairLimits, ScalarCandidate};
19
20pub struct RuntimeProviderRegistry<S> {
24 groups: Vec<RuntimeScalarGroupProviderBinding<S>>,
25 repairs: Vec<RuntimeConflictRepairProviderBinding<S>>,
26 static_groups: Vec<StaticScalarGroupProviderBinding<S>>,
27 static_repairs: Vec<StaticConflictRepairProviderBinding<S>>,
28 error_boundary: Arc<dyn RuntimeHostProviderErrorBoundary>,
29 resolver: Option<RuntimeProviderSlotResolver<S>>,
30}
31
32impl<S> Default for RuntimeProviderRegistry<S> {
33 fn default() -> Self {
34 Self {
35 groups: Vec::new(),
36 repairs: Vec::new(),
37 static_groups: Vec::new(),
38 static_repairs: Vec::new(),
39 error_boundary: Arc::new(PanicProviderErrorBoundary),
40 resolver: None,
41 }
42 }
43}
44
45impl<S> Clone for RuntimeProviderRegistry<S> {
46 fn clone(&self) -> Self {
47 Self {
48 groups: self.groups.clone(),
49 repairs: self.repairs.clone(),
50 static_groups: self.static_groups.clone(),
51 static_repairs: self.static_repairs.clone(),
52 error_boundary: Arc::clone(&self.error_boundary),
53 resolver: self.resolver.clone(),
54 }
55 }
56}
57
58impl<S> fmt::Debug for RuntimeProviderRegistry<S> {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 f.debug_struct("RuntimeProviderRegistry")
61 .field("groups", &self.groups)
62 .field("repairs", &self.repairs)
63 .field("static_groups", &self.static_groups)
64 .field("static_repairs", &self.static_repairs)
65 .field("resolver_bound", &self.resolver.is_some())
66 .finish()
67 }
68}
69
70impl<S: 'static> RuntimeProviderRegistry<S> {
71 pub fn new(
72 groups: Vec<RuntimeScalarGroupProviderBinding<S>>,
73 repairs: Vec<RuntimeConflictRepairProviderBinding<S>>,
74 error_boundary: Arc<dyn RuntimeHostProviderErrorBoundary>,
75 ) -> Result<Self, String> {
76 let mut group_names = HashSet::new();
77 let mut previous_group_schema_index = None;
78 for group in &groups {
79 if group.group_name.is_empty() {
80 return Err("runtime provider registry has an empty group name".to_string());
81 }
82 if previous_group_schema_index.is_some_and(|previous| previous >= group.declared_index)
83 {
84 return Err(format!(
85 "runtime provider group `{}` has non-monotonic declared schema index {}",
86 group.group_name, group.declared_index
87 ));
88 }
89 previous_group_schema_index = Some(group.declared_index);
90 if !group_names.insert(Arc::clone(&group.group_name)) {
91 return Err(format!(
92 "runtime provider registry declares callback group `{}` more than once",
93 group.group_name
94 ));
95 }
96 }
97 let mut previous_repair_schema_index = None;
98 for repair in &repairs {
99 if previous_repair_schema_index
100 .is_some_and(|previous| previous >= repair.declared_index)
101 {
102 return Err(format!(
103 "runtime repair provider has non-monotonic declared schema index {}",
104 repair.declared_index
105 ));
106 }
107 previous_repair_schema_index = Some(repair.declared_index);
108 }
109 Ok(Self {
110 groups,
111 repairs,
112 static_groups: Vec::new(),
113 static_repairs: Vec::new(),
114 error_boundary,
115 resolver: None,
116 })
117 }
118
119 pub fn is_empty(&self) -> bool {
120 self.groups.is_empty()
121 && self.repairs.is_empty()
122 && self.static_groups.is_empty()
123 && self.static_repairs.is_empty()
124 }
125
126 pub fn groups(&self) -> &[RuntimeScalarGroupProviderBinding<S>] {
127 &self.groups
128 }
129
130 pub fn repairs(&self) -> &[RuntimeConflictRepairProviderBinding<S>] {
131 &self.repairs
132 }
133
134 pub fn static_groups(&self) -> &[StaticScalarGroupProviderBinding<S>] {
135 &self.static_groups
136 }
137
138 pub fn static_repairs(&self) -> &[StaticConflictRepairProviderBinding<S>] {
139 &self.static_repairs
140 }
141
142 pub fn group_indices(&self, group_name: &str) -> Vec<usize> {
143 self.groups
144 .iter()
145 .enumerate()
146 .filter_map(|(index, group)| (group.group_name.as_ref() == group_name).then_some(index))
147 .collect()
148 }
149
150 pub fn declares_constraint(&self, handle: RuntimeProviderHandle, constraint: &str) -> bool {
152 match handle {
153 RuntimeProviderHandle::CallbackRepair(index) => self
154 .repairs
155 .get(index)
156 .unwrap_or_else(|| {
157 panic!("compiled callback repair provider index {index} no longer exists")
158 })
159 .declared_constraints
160 .iter()
161 .any(|name| name.as_ref() == constraint),
162 RuntimeProviderHandle::StaticRepair(index) => {
163 self.static_repairs
164 .get(index)
165 .unwrap_or_else(|| {
166 panic!("compiled static repair provider index {index} no longer exists")
167 })
168 .repair
169 .constraint_name()
170 == constraint
171 }
172 RuntimeProviderHandle::CallbackGroup(_) | RuntimeProviderHandle::StaticGroup(_) => {
173 false
174 }
175 }
176 }
177
178 pub fn declares_any_constraint(
179 &self,
180 handle: RuntimeProviderHandle,
181 constraints: &[String],
182 ) -> bool {
183 constraints
184 .iter()
185 .any(|constraint| self.declares_constraint(handle, constraint))
186 }
187
188 pub(crate) fn freeze(
189 &mut self,
190 scalar_slots: &[RuntimeScalarSlot<S>],
191 scalar_groups: &[ScalarGroupBinding<S>],
192 conflict_repairs: &[ConflictRepair<S>],
193 ) -> Result<(), String> {
194 self.static_groups.clear();
195 self.static_repairs.clear();
196 for (declared_index, group) in scalar_groups.iter().enumerate() {
197 let ScalarGroupBindingKind::Candidates { candidate_provider } = &group.kind else {
198 continue;
199 };
200 self.static_groups.push(StaticScalarGroupProviderBinding {
201 declared_index,
202 group_name: group.group_name,
203 provider: *candidate_provider,
204 declared_limits: group.limits,
205 });
206 }
207 for (declared_index, repair) in conflict_repairs.iter().enumerate() {
208 self.static_repairs
209 .push(StaticConflictRepairProviderBinding {
210 declared_index,
211 repair: *repair,
212 });
213 }
214 if self.is_empty() {
215 return Ok(());
216 }
217 self.resolver = Some(RuntimeProviderSlotResolver::new(scalar_slots.to_vec())?);
218 Ok(())
219 }
220
221 fn resolver(&self) -> &RuntimeProviderSlotResolver<S> {
222 self.resolver.as_ref().unwrap_or_else(|| {
223 panic!("runtime provider registry was invoked before descriptor resolution")
224 })
225 }
226
227 pub fn pull_callback_raw(
229 &self,
230 handle: RuntimeProviderHandle,
231 solution: &S,
232 limits: RuntimeProviderLimits,
233 ) -> Vec<RawProviderCandidate> {
234 let _ = self.resolver();
235 let callback = match handle {
236 RuntimeProviderHandle::CallbackGroup(index) => self
237 .groups
238 .get(index)
239 .unwrap_or_else(|| {
240 panic!("compiled callback group provider index {index} no longer exists")
241 })
242 .callback
243 .as_ref(),
244 RuntimeProviderHandle::CallbackRepair(index) => self
245 .repairs
246 .get(index)
247 .unwrap_or_else(|| {
248 panic!("compiled callback repair provider index {index} no longer exists")
249 })
250 .callback
251 .as_ref(),
252 RuntimeProviderHandle::StaticGroup(_) | RuntimeProviderHandle::StaticRepair(_) => {
253 panic!("static providers must use their concrete pull path")
254 }
255 };
256 callback.pull(solution, limits)
257 }
258
259 pub(crate) fn pull_static_group(
260 &self,
261 index: usize,
262 solution: &S,
263 value_candidate_limit: Option<usize>,
264 max_moves_per_step: Option<usize>,
265 ) -> Vec<ScalarCandidate<S>> {
266 let _ = self.resolver();
267 let binding = self.static_groups.get(index).unwrap_or_else(|| {
268 panic!("compiled static group provider index {index} no longer exists")
269 });
270 pull_static_group(binding, solution, value_candidate_limit, max_moves_per_step)
271 }
272
273 pub(crate) fn pull_static_repair(
274 &self,
275 index: usize,
276 solution: &S,
277 limits: RepairLimits,
278 ) -> Vec<RepairCandidate<S>> {
279 let _ = self.resolver();
280 let binding = self.static_repairs.get(index).unwrap_or_else(|| {
281 panic!("compiled static repair provider index {index} no longer exists")
282 });
283 pull_static_repair(binding, solution, limits)
284 }
285
286 pub fn normalize_or_raise(
290 &self,
291 solution: &S,
292 raw: Vec<RawProviderCandidate>,
293 allowed_slots: &[RuntimeScalarSlotId],
294 state: &mut ProviderNormalizationState,
295 reasons: &mut ProviderReasonArena,
296 ) -> Vec<ResolvedProviderCandidate<S>> {
297 match self.resolver().resolve_and_normalize_with_state(
298 solution,
299 raw,
300 allowed_slots,
301 state,
302 reasons,
303 ) {
304 Ok(candidates) => candidates,
305 Err(error) => self.error_boundary.raise(error),
306 }
307 }
308
309 pub(crate) fn normalize_static_group(
310 &self,
311 solution: &S,
312 candidates: Vec<ScalarCandidate<S>>,
313 allowed_slots: &[RuntimeScalarSlotId],
314 state: &mut ProviderNormalizationState,
315 reasons: &mut ProviderReasonArena,
316 ) -> Vec<ResolvedProviderCandidate<S>> {
317 self.resolver()
318 .resolve_static_group_and_normalize_with_state(
319 solution,
320 candidates,
321 allowed_slots,
322 state,
323 reasons,
324 )
325 .unwrap_or_else(|error| panic!("{error}"))
326 }
327
328 pub(crate) fn normalize_static_repair(
329 &self,
330 solution: &S,
331 candidates: Vec<RepairCandidate<S>>,
332 allowed_slots: &[RuntimeScalarSlotId],
333 state: &mut ProviderNormalizationState,
334 reasons: &mut ProviderReasonArena,
335 ) -> Vec<ResolvedProviderCandidate<S>> {
336 self.resolver()
337 .resolve_static_repair_and_normalize_with_state(
338 solution,
339 candidates,
340 allowed_slots,
341 state,
342 reasons,
343 )
344 .unwrap_or_else(|error| panic!("{error}"))
345 }
346}