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 dimensions",
139 problem_type.canonical_name
140 )
141 .into());
142 }
143
144 Self::from_map(problem_type, variant)
145 }
146
147 pub fn name(&self) -> &str {
149 &self.name
150 }
151
152 pub fn variant(&self) -> &BTreeMap<String, String> {
154 &self.variant
155 }
156
157 pub fn to_export_ref(&self) -> crate::export::ProblemRef {
159 crate::export::ProblemRef {
160 name: self.name.clone(),
161 variant: self.variant.clone(),
162 }
163 }
164}
165
166pub fn parse_catalog_problem_ref(input: &str) -> Result<ProblemRef, ConstructionError> {
171 let parts: Vec<&str> = input.split('/').collect();
172 let raw_name = parts[0];
173 let values: Vec<&str> = parts[1..].to_vec();
174
175 let problem_type = super::problem_type::find_problem_type_by_alias(raw_name)
177 .ok_or_else(|| format!("Unknown problem type: \"{raw_name}\""))?;
178
179 let effective_values: Vec<String> = values.iter().map(|s| s.to_string()).collect();
180
181 ProblemRef::from_values(&problem_type, &effective_values)
182}
183
184pub fn require_graph_variant(
189 graph: &crate::rules::ReductionGraph,
190 problem_ref: &ProblemRef,
191) -> Result<crate::export::ProblemRef, ConstructionError> {
192 let known_variants = graph.variants_for(problem_ref.name());
193 if known_variants.iter().any(|v| v == problem_ref.variant()) {
194 return Ok(problem_ref.to_export_ref());
195 }
196
197 Err(format!(
198 "Variant {:?} of {} is schema-valid but not reachable in the reduction graph. \
199 Known graph variants: {:?}",
200 problem_ref.variant(),
201 problem_ref.name(),
202 known_variants
203 )
204 .into())
205}