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