problemreductions/registry/
problem_ref.rs1use super::problem_type::ProblemType;
4use super::ConstructionError;
5use std::collections::BTreeMap;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct ProblemRef {
13 name: String,
15 variant: BTreeMap<String, String>,
17}
18
19impl ProblemRef {
20 pub fn from_values<I, S>(
30 problem_type: &ProblemType,
31 values: I,
32 ) -> Result<Self, ConstructionError>
33 where
34 I: IntoIterator<Item = S>,
35 S: AsRef<str>,
36 {
37 let mut variant: BTreeMap<String, String> = problem_type.default_variant();
39 let mut matched_dims: Vec<bool> = vec![false; problem_type.dimensions.len()];
40
41 for value in values {
42 let val = value.as_ref();
43 let dim_idx = problem_type
45 .dimensions
46 .iter()
47 .enumerate()
48 .find(|(i, dim)| !matched_dims[*i] && dim.allowed_values.contains(&val))
49 .map(|(i, _)| i);
50
51 match dim_idx {
52 Some(idx) => {
53 matched_dims[idx] = true;
54 let dim = &problem_type.dimensions[idx];
55 variant.insert(dim.key.to_string(), val.to_string());
56 }
57 None => {
58 let known: Vec<&str> = problem_type
59 .dimensions
60 .iter()
61 .flat_map(|d| d.allowed_values.iter().copied())
62 .collect();
63 return Err(format!(
64 "Unknown variant value \"{val}\" for {}. Known variants: {known:?}",
65 problem_type.canonical_name,
66 )
67 .into());
68 }
69 }
70 }
71
72 Ok(Self {
73 name: problem_type.canonical_name.to_string(),
74 variant,
75 })
76 }
77
78 pub fn from_map(
80 problem_type: &ProblemType,
81 variant: BTreeMap<String, String>,
82 ) -> Result<Self, ConstructionError> {
83 for (key, value) in &variant {
85 let dim = problem_type
86 .dimensions
87 .iter()
88 .find(|d| d.key == key.as_str())
89 .ok_or_else(|| {
90 format!(
91 "Unknown dimension \"{key}\" for {}",
92 problem_type.canonical_name
93 )
94 })?;
95 if !dim.allowed_values.contains(&value.as_str()) {
96 return Err(format!(
97 "Unknown value \"{value}\" for dimension \"{key}\" of {}. Known variants: {:?}",
98 problem_type.canonical_name, dim.allowed_values
99 )
100 .into());
101 }
102 }
103
104 let mut full_variant = problem_type.default_variant();
106 full_variant.extend(variant);
107
108 Ok(Self {
109 name: problem_type.canonical_name.to_string(),
110 variant: full_variant,
111 })
112 }
113
114 pub fn from_prefix_map(
120 problem_type: &ProblemType,
121 variant: BTreeMap<String, String>,
122 ) -> Result<Self, ConstructionError> {
123 if !problem_type.dimensions.is_empty() && variant.is_empty() {
124 return Err(format!(
125 "Variant for {} must specify its first dimension \"{}\"",
126 problem_type.canonical_name, problem_type.dimensions[0].key
127 )
128 .into());
129 }
130
131 let supplied = variant.len();
132 if supplied > problem_type.dimensions.len()
133 || problem_type.dimensions[..supplied]
134 .iter()
135 .any(|dimension| !variant.contains_key(dimension.key))
136 {
137 return Err(format!(
138 "Variant for {} must specify a prefix of its dimension keys: {}",
139 problem_type.canonical_name,
140 problem_type
141 .dimensions
142 .iter()
143 .map(|dimension| dimension.key)
144 .collect::<Vec<_>>()
145 .join(", ")
146 )
147 .into());
148 }
149
150 Self::from_map(problem_type, variant)
151 }
152
153 pub fn name(&self) -> &str {
155 &self.name
156 }
157
158 pub fn variant(&self) -> &BTreeMap<String, String> {
160 &self.variant
161 }
162
163 pub fn to_export_ref(&self) -> crate::export::ProblemRef {
165 crate::export::ProblemRef {
166 name: self.name.clone(),
167 variant: self.variant.clone(),
168 }
169 }
170}
171
172pub fn parse_catalog_problem_ref(input: &str) -> Result<ProblemRef, ConstructionError> {
177 let parts: Vec<&str> = input.split('/').collect();
178 let raw_name = parts[0];
179 let values: Vec<&str> = parts[1..].to_vec();
180
181 let problem_type = super::problem_type::find_problem_type_by_alias(raw_name)
183 .ok_or_else(|| format!("Unknown problem type: \"{raw_name}\""))?;
184
185 let effective_values: Vec<String> = values.iter().map(|s| s.to_string()).collect();
186
187 ProblemRef::from_values(&problem_type, &effective_values)
188}
189
190pub fn require_graph_variant(
195 graph: &crate::rules::ReductionGraph,
196 problem_ref: &ProblemRef,
197) -> Result<crate::export::ProblemRef, ConstructionError> {
198 let known_variants = graph.variants_for(problem_ref.name());
199 if known_variants.iter().any(|v| v == problem_ref.variant()) {
200 return Ok(problem_ref.to_export_ref());
201 }
202
203 Err(format!(
204 "Variant {:?} of {} is schema-valid but not reachable in the reduction graph. \
205 Known graph variants: {:?}",
206 problem_ref.variant(),
207 problem_ref.name(),
208 known_variants
209 )
210 .into())
211}