1use crate::rules::registry::{ParameterContractError, ReductionParameterContract};
4use crate::rules::ReductionGraph;
5use crate::traits::Problem;
6use serde::{Deserialize, Serialize};
7use std::collections::BTreeMap;
8use std::fs;
9use std::path::Path;
10
11#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
13pub struct ProblemSide {
14 pub problem: String,
16 pub variant: BTreeMap<String, String>,
18 pub instance: serde_json::Value,
20}
21
22impl ProblemSide {
23 pub fn from_problem<P>(problem: &P) -> Self
25 where
26 P: Problem + Serialize,
27 {
28 Self {
29 problem: P::NAME.to_string(),
30 variant: variant_to_map(P::variant()),
31 instance: serde_json::to_value(problem).expect("Failed to serialize problem instance"),
32 }
33 }
34
35 pub fn problem_ref(&self) -> ProblemRef {
37 ProblemRef {
38 name: self.problem.clone(),
39 variant: self.variant.clone(),
40 }
41 }
42}
43
44#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
46pub struct ProblemRef {
47 pub name: String,
48 pub variant: BTreeMap<String, String>,
49}
50
51#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
53pub struct SolutionPair {
54 pub source_config: serde_json::Value,
55 pub target_config: serde_json::Value,
56}
57
58#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
60pub struct RuleExample {
61 pub source: ProblemSide,
62 pub target: ProblemSide,
63 pub solutions: Vec<SolutionPair>,
64}
65
66#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
68pub struct ModelExample {
69 pub problem: String,
70 pub variant: BTreeMap<String, String>,
71 pub instance: serde_json::Value,
72 pub optimal_config: serde_json::Value,
73 pub optimal_value: serde_json::Value,
74}
75
76impl ModelExample {
77 pub fn new(
78 problem: &str,
79 variant: BTreeMap<String, String>,
80 instance: serde_json::Value,
81 optimal_config: serde_json::Value,
82 optimal_value: serde_json::Value,
83 ) -> Self {
84 Self {
85 problem: problem.to_string(),
86 variant,
87 instance,
88 optimal_config,
89 optimal_value,
90 }
91 }
92
93 pub fn problem_ref(&self) -> ProblemRef {
94 ProblemRef {
95 name: self.problem.clone(),
96 variant: self.variant.clone(),
97 }
98 }
99}
100
101#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
103pub struct RuleDb {
104 pub rules: Vec<RuleExample>,
105}
106
107#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
109pub struct ModelDb {
110 pub models: Vec<ModelExample>,
111}
112
113#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
115pub struct ExampleDb {
116 pub models: Vec<ModelExample>,
117 pub rules: Vec<RuleExample>,
118}
119
120pub fn lookup_parameter_contract(
122 source_name: &str,
123 source_variant: &BTreeMap<String, String>,
124 target_name: &str,
125 target_variant: &BTreeMap<String, String>,
126) -> Result<Option<ReductionParameterContract>, ParameterContractError> {
127 let graph = ReductionGraph::new();
128 let Some(matched) = graph.find_entry(source_name, source_variant, target_name, target_variant)
129 else {
130 return Ok(None);
131 };
132 matched.parameter_contract.map(Some)
133}
134
135pub fn variant_to_map(variant: Vec<(&str, &str)>) -> BTreeMap<String, String> {
140 variant
141 .into_iter()
142 .map(|(k, v)| {
143 let value = if k == "graph" && v.is_empty() {
144 "SimpleGraph".to_string()
145 } else {
146 v.to_string()
147 };
148 (k.to_string(), value)
149 })
150 .collect()
151}
152
153fn write_json_file<T: Serialize>(dir: &Path, name: &str, payload: &T) {
154 fs::create_dir_all(dir).expect("Failed to create examples directory");
155 let path = dir.join(format!("{name}.json"));
156 let json = serde_json::to_string_pretty(payload).expect("Failed to serialize example");
157 fs::write(&path, json).expect("Failed to write example JSON");
158 println!("Exported: {}", path.display());
159}
160
161fn render_compact_array<T: Serialize>(items: &[T]) -> String {
162 if items.is_empty() {
163 "[]".to_string()
164 } else {
165 let rows = items
166 .iter()
167 .map(|item| {
168 format!(
169 " {}",
170 serde_json::to_string(item).expect("Failed to serialize example entry")
171 )
172 })
173 .collect::<Vec<_>>()
174 .join(",\n");
175 format!("[\n{rows}\n ]")
176 }
177}
178
179fn write_example_db_file(dir: &Path, db: &ExampleDb) {
180 fs::create_dir_all(dir).expect("Failed to create examples directory");
181 let path = dir.join("examples.json");
182 let json = format!(
183 "{{\n \"models\": {},\n \"rules\": {}\n}}\n",
184 render_compact_array(&db.models),
185 render_compact_array(&db.rules)
186 );
187 fs::write(&path, json).expect("Failed to write example JSON");
188 println!("Exported: {}", path.display());
189}
190
191pub fn write_rule_example_to(dir: &Path, name: &str, example: &RuleExample) {
193 write_json_file(dir, name, example);
194}
195
196pub fn write_model_example_to(dir: &Path, name: &str, example: &ModelExample) {
198 write_json_file(dir, name, example);
199}
200
201pub fn write_rule_db_to(dir: &Path, db: &RuleDb) {
203 write_json_file(dir, "rules", db);
204}
205
206pub fn write_model_db_to(dir: &Path, db: &ModelDb) {
208 write_json_file(dir, "models", db);
209}
210
211pub fn write_example_db_to(dir: &Path, db: &ExampleDb) {
213 write_example_db_file(dir, db);
214}
215
216#[cfg(test)]
217#[path = "unit_tests/export.rs"]
218mod tests;