problemreductions/registry/
problem_ref.rs1use super::problem_type::ProblemType;
4use std::collections::BTreeMap;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct ProblemRef {
12 name: String,
14 variant: BTreeMap<String, String>,
16}
17
18impl ProblemRef {
19 pub fn from_values<I, S>(problem_type: &ProblemType, values: I) -> Result<Self, String>
29 where
30 I: IntoIterator<Item = S>,
31 S: AsRef<str>,
32 {
33 let mut variant: BTreeMap<String, String> = problem_type.default_variant();
35 let mut matched_dims: Vec<bool> = vec![false; problem_type.dimensions.len()];
36
37 for value in values {
38 let val = value.as_ref();
39 let dim_idx = problem_type
41 .dimensions
42 .iter()
43 .enumerate()
44 .find(|(i, dim)| !matched_dims[*i] && dim.allowed_values.contains(&val))
45 .map(|(i, _)| i);
46
47 match dim_idx {
48 Some(idx) => {
49 matched_dims[idx] = true;
50 let dim = &problem_type.dimensions[idx];
51 variant.insert(dim.key.to_string(), val.to_string());
52 }
53 None => {
54 let known: Vec<&str> = problem_type
55 .dimensions
56 .iter()
57 .flat_map(|d| d.allowed_values.iter().copied())
58 .collect();
59 return Err(format!(
60 "Unknown variant value \"{val}\" for {}. Known variants: {known:?}",
61 problem_type.canonical_name,
62 ));
63 }
64 }
65 }
66
67 Ok(Self {
68 name: problem_type.canonical_name.to_string(),
69 variant,
70 })
71 }
72
73 pub fn from_map(
75 problem_type: &ProblemType,
76 variant: BTreeMap<String, String>,
77 ) -> Result<Self, String> {
78 for (key, value) in &variant {
80 let dim = problem_type
81 .dimensions
82 .iter()
83 .find(|d| d.key == key.as_str())
84 .ok_or_else(|| {
85 format!(
86 "Unknown dimension \"{key}\" for {}",
87 problem_type.canonical_name
88 )
89 })?;
90 if !dim.allowed_values.contains(&value.as_str()) {
91 return Err(format!(
92 "Unknown value \"{value}\" for dimension \"{key}\" of {}. Known variants: {:?}",
93 problem_type.canonical_name, dim.allowed_values
94 ));
95 }
96 }
97
98 let mut full_variant = problem_type.default_variant();
100 full_variant.extend(variant);
101
102 Ok(Self {
103 name: problem_type.canonical_name.to_string(),
104 variant: full_variant,
105 })
106 }
107
108 pub fn name(&self) -> &str {
110 &self.name
111 }
112
113 pub fn variant(&self) -> &BTreeMap<String, String> {
115 &self.variant
116 }
117
118 pub fn to_export_ref(&self) -> crate::export::ProblemRef {
120 crate::export::ProblemRef {
121 name: self.name.clone(),
122 variant: self.variant.clone(),
123 }
124 }
125}
126
127pub fn parse_catalog_problem_ref(input: &str) -> Result<ProblemRef, String> {
132 let parts: Vec<&str> = input.split('/').collect();
133 let raw_name = parts[0];
134 let values: Vec<&str> = parts[1..].to_vec();
135
136 let problem_type = super::problem_type::find_problem_type_by_alias(raw_name)
138 .ok_or_else(|| format!("Unknown problem type: \"{raw_name}\""))?;
139
140 let effective_values: Vec<String> = values.iter().map(|s| s.to_string()).collect();
141
142 ProblemRef::from_values(&problem_type, &effective_values)
143}
144
145pub fn require_graph_variant(
150 graph: &crate::rules::ReductionGraph,
151 problem_ref: &ProblemRef,
152) -> Result<crate::export::ProblemRef, String> {
153 let known_variants = graph.variants_for(problem_ref.name());
154 if known_variants.iter().any(|v| v == problem_ref.variant()) {
155 return Ok(problem_ref.to_export_ref());
156 }
157
158 Err(format!(
159 "Variant {:?} of {} is schema-valid but not reachable in the reduction graph. \
160 Known graph variants: {:?}",
161 problem_ref.variant(),
162 problem_ref.name(),
163 known_variants
164 ))
165}