1#[allow(unused_imports)]
2use crate::prelude::*;
3use serde::{Deserialize, Serialize};
4
5#[cfg(target_arch = "wasm32")]
6use crate::pipe::run_pipe;
7#[cfg(target_arch = "wasm32")]
8use crate::runtime_builtin::JsFunction;
9use crate::{IntOrBoolValue, LinearModel, LpSolution, MILPValue};
10#[cfg(target_arch = "wasm32")]
11use {
12 crate::parser::model_transformer::Model,
13 crate::parser::pre_model::PreModel,
14 crate::pipe::pipe_definitions::{PipeDataType, PipeError, Pipeable, PipeableData},
15 crate::pipe::pipe_executors::{
16 AutoSolverPipe, BinarySolverPipe, CompilerPipe, IntegerBinarySolverPipe, LinearModelPipe,
17 MILPSolverPipe, ModelPipe, Pipes, PreModelPipe, RealSolver, StandardLinearModelPipe,
18 StepByStepSimplexPipe, TableauPipe,
19 },
20 crate::pipe::PipeContext,
21 crate::solvers::{OptimalTableau, OptimalTableauWithSteps, Tableau},
22 crate::transformers::StandardLinearModel,
23 crate::RoocParser,
24 crate::{Constant, Primitive},
25};
26
27#[cfg(target_arch = "wasm32")]
28#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
29pub struct WasmPipeRunner {
30 pipes: Vec<Box<dyn Pipeable>>,
31}
32
33#[cfg(target_arch = "wasm32")]
34#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
35pub struct JsPipable {
36 function: Function,
37}
38#[cfg(target_arch = "wasm32")]
39#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
40impl JsPipable {
41 pub fn new_wasm(function: Function) -> JsPipable {
42 JsPipable { function }
43 }
44}
45
46#[derive(Serialize, Deserialize)]
47#[serde(tag = "type", content = "value")]
48enum JsPipableData {
49 String(String),
50 LinearModel(LinearModel),
51 BinarySolution(LpSolution<bool>),
52 IntegerBinarySolution(LpSolution<IntOrBoolValue>),
53 RealSolution(LpSolution<f64>),
54 MILPSolution(LpSolution<MILPValue>),
55}
56
57#[cfg(target_arch = "wasm32")]
58impl Pipeable for JsPipable {
59 fn pipe(
60 &self,
61 data: &mut PipeableData,
62 _pipe_context: &PipeContext,
63 ) -> Result<PipeableData, PipeError> {
64 let js_pipable = match data {
65 PipeableData::String(s) => JsPipableData::String(s.clone()),
66 PipeableData::LinearModel(lm) => JsPipableData::LinearModel(lm.clone()),
67 PipeableData::BinarySolution(sol) => JsPipableData::BinarySolution(sol.clone()),
68 PipeableData::IntegerBinarySolution(sol) => {
69 JsPipableData::IntegerBinarySolution(sol.clone())
70 }
71 PipeableData::RealSolution(sol) => JsPipableData::RealSolution(sol.clone()),
72 PipeableData::MILPSolution(sol) => JsPipableData::MILPSolution(sol.clone()),
73 _ => return Err(PipeError::Other("JsPipable data must be one of: String, LinearModel, BinarySolution, IntegerBinarySolution, RealSolution, MILPSolution".to_string()))
74 };
75 let js_pipable =
76 serialize_json_compatible(&js_pipable).map_err(|e| PipeError::Other(e.to_string()))?;
77 let js_args = js_sys::Array::new();
78 js_args.push(&js_pipable);
79 let result = self.function.apply(&JsValue::NULL, &js_args).map_err(|e| {
80 PipeError::Other(
81 e.as_string()
82 .unwrap_or("Error thrown in function".to_string()),
83 )
84 })?;
85 let result: JsPipableData =
86 serde_wasm_bindgen::from_value(result).map_err(|e| PipeError::Other(e.to_string()))?;
87 let pipe_result = match result {
88 JsPipableData::String(s) => PipeableData::String(s),
89 JsPipableData::LinearModel(lm) => PipeableData::LinearModel(lm),
90 JsPipableData::BinarySolution(sol) => PipeableData::BinarySolution(sol),
91 JsPipableData::IntegerBinarySolution(sol) => PipeableData::IntegerBinarySolution(sol),
92 JsPipableData::RealSolution(sol) => PipeableData::RealSolution(sol),
93 JsPipableData::MILPSolution(sol) => PipeableData::MILPSolution(sol),
94 };
95 Ok(pipe_result)
96 }
97}
98
99#[cfg(target_arch = "wasm32")]
100#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
101impl WasmPipeRunner {
102 pub fn new_wasm() -> WasmPipeRunner {
103 WasmPipeRunner { pipes: vec![] }
104 }
105
106 pub fn add_step_by_name(&mut self, step: Pipes) {
107 let step: Box<dyn Pipeable> = match step {
108 Pipes::CompilerPipe => Box::new(CompilerPipe::new()),
109 Pipes::PreModelPipe => Box::new(PreModelPipe::new()),
110 Pipes::ModelPipe => Box::new(ModelPipe::new()),
111 Pipes::LinearModelPipe => Box::new(LinearModelPipe::new()),
112 Pipes::StandardLinearModelPipe => Box::new(StandardLinearModelPipe::new()),
113 Pipes::TableauPipe => Box::new(TableauPipe::new()),
114 Pipes::RealPipe => Box::new(RealSolver::new()),
115 Pipes::StepByStepSimplexPipe => Box::new(StepByStepSimplexPipe::new()),
116 Pipes::BinarySolverPipe => Box::new(BinarySolverPipe::new()),
117 Pipes::IntegerBinarySolverPipe => Box::new(IntegerBinarySolverPipe::new()),
118 Pipes::MILPSolverPipe => Box::new(MILPSolverPipe::new()),
119 Pipes::AutoSolverPipe => Box::new(AutoSolverPipe::new()),
120 };
121 self.pipes.push(step);
122 }
123
124 pub fn add_step_with_fn(&mut self, function: JsPipable) {
125 self.pipes.push(Box::new(function));
126 }
127
128 pub fn wasm_run_from_string(
129 &self,
130 data: String,
131 constants: JsValue,
132 fns: Vec<JsFunction>,
133 ) -> Result<Vec<WasmPipableData>, WasmPipeError> {
134 let data = PipeableData::String(data);
135 let constants: Vec<(String, Primitive)> = serde_wasm_bindgen::from_value(constants)
136 .map_err(|e| WasmPipeError::new(PipeError::Other(e.to_string()), vec![]))?;
137 let constants = constants
138 .into_iter()
139 .map(|v| Constant::from_primitive(&v.0, v.1))
140 .collect();
141 let fns = js_value_to_fns_map(fns);
142 match run_pipe(&self.pipes, data, &PipeContext::new(constants, &fns)) {
143 Ok(results) => Ok(results.into_iter().map(WasmPipableData::new).collect()),
144 Err((e, results)) => {
145 let results: Vec<WasmPipableData> =
146 results.into_iter().map(WasmPipableData::new).collect();
147 Err(WasmPipeError::new(e, results))
148 }
149 }
150 }
151}
152
153#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
154#[cfg(target_arch = "wasm32")]
155pub struct WasmPipeError {
156 error: PipeError,
157 context: Vec<WasmPipableData>,
158}
159
160#[cfg(target_arch = "wasm32")]
161impl WasmPipeError {
162 pub fn new(error: PipeError, context: Vec<WasmPipableData>) -> WasmPipeError {
163 WasmPipeError { error, context }
164 }
165}
166
167#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
168#[cfg(target_arch = "wasm32")]
169impl WasmPipeError {
170 pub fn wasm_get_error(&self) -> String {
171 self.error.to_string()
172 }
173 pub fn wasm_get_context(&self) -> Vec<WasmPipableData> {
174 self.context.clone()
175 }
176 pub fn wasm_to_context(self) -> Vec<WasmPipableData> {
177 self.context
178 }
179}
180
181#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
182#[cfg(target_arch = "wasm32")]
183#[derive(Debug, Clone)]
184pub struct WasmPipableData {
185 data: PipeableData,
186}
187
188#[cfg(target_arch = "wasm32")]
189impl WasmPipableData {
190 pub fn new(data: PipeableData) -> WasmPipableData {
191 WasmPipableData { data }
192 }
193}
194
195#[cfg(target_arch = "wasm32")]
196impl From<WasmPipableData> for PipeableData {
197 fn from(data: WasmPipableData) -> Self {
198 data.data
199 }
200}
201#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
202#[allow(clippy::wrong_self_convention)]
203#[cfg(target_arch = "wasm32")]
204impl WasmPipableData {
205 pub fn wasm_get_type(&self) -> PipeDataType {
206 self.data.get_type()
207 }
208
209 pub fn to_string_data(self) -> Result<String, JsValue> {
211 self.data
212 .to_string_data()
213 .map_err(|e| JsValue::from_str(&e.to_string()))
214 }
215 pub fn to_parser(self) -> Result<RoocParser, JsValue> {
216 self.data
217 .to_parser()
218 .map_err(|e| JsValue::from_str(&e.to_string()))
219 }
220 pub fn to_pre_model(self) -> Result<PreModel, JsValue> {
221 self.data
222 .to_pre_model()
223 .map_err(|e| JsValue::from_str(&e.to_string()))
224 }
225 pub fn to_model(self) -> Result<Model, JsValue> {
226 self.data
227 .to_model()
228 .map_err(|e| JsValue::from_str(&e.to_string()))
229 }
230 pub fn to_linear_model(self) -> Result<LinearModel, JsValue> {
231 self.data
232 .to_linear_model()
233 .map_err(|e| JsValue::from_str(&e.to_string()))
234 }
235 pub fn to_standard_linear_model(self) -> Result<StandardLinearModel, JsValue> {
236 self.data
237 .to_standard_linear_model()
238 .map_err(|e| JsValue::from_str(&e.to_string()))
239 }
240 pub fn to_tableau(self) -> Result<Tableau, JsValue> {
241 self.data
242 .to_tableau()
243 .map_err(|e| JsValue::from_str(&e.to_string()))
244 }
245 pub fn to_optimal_tableau(self) -> Result<OptimalTableau, JsValue> {
246 self.data
247 .to_optimal_tableau()
248 .map_err(|e| JsValue::from_str(&e.to_string()))
249 }
250 pub fn to_optimal_tableau_with_steps(self) -> Result<OptimalTableauWithSteps, JsValue> {
251 self.data
252 .to_optimal_tableau_with_steps()
253 .map_err(|e| JsValue::from_str(&e.to_string()))
254 }
255 pub fn to_binary_solution(self) -> Result<JsValue, JsValue> {
256 self.data
257 .to_binary_solution()
258 .map(|s| serde_wasm_bindgen::to_value(&s).unwrap())
259 .map_err(|e| JsValue::from_str(&e.to_string()))
260 }
261 pub fn to_integer_binary_solution(self) -> Result<JsValue, JsValue> {
262 self.data
263 .to_integer_binary_solution()
264 .map(|s| serde_wasm_bindgen::to_value(&s).unwrap())
265 .map_err(|e| JsValue::from_str(&e.to_string()))
266 }
267
268 pub fn to_real_solution(self) -> Result<JsValue, JsValue> {
269 self.data
270 .to_real_solution()
271 .map(|s| serde_wasm_bindgen::to_value(&s).unwrap())
272 .map_err(|e| JsValue::from_str(&e.to_string()))
273 }
274
275 pub fn to_milp_solution(self) -> Result<JsValue, JsValue> {
276 self.data
277 .to_milp_solution()
278 .map(|s| serde_wasm_bindgen::to_value(&s).unwrap())
279 .map_err(|e| JsValue::from_str(&e.to_string()))
280 }
281}