Skip to content

Commit 60c2707

Browse files
WIP: Implemented BigDecimal for combining numbers
1 parent 9749435 commit 60c2707

File tree

8 files changed

+290
-90
lines changed

8 files changed

+290
-90
lines changed

examples/simple-url-call/Cargo.lock

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/simple-url-call/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ opt-level = 'z'
1313
[dependencies]
1414
serde_json = "1.0"
1515
serde = { version = "1.0", features = ["derive"] }
16-
jsonpath-rust = "0.1.0"
16+
jsonpath-rust = "0.1.0"
17+
bigdecimal = "0.3.0"

examples/simple-url-call/src/flux/mod.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::str;
2+
use std::process;
23
use serde::Deserialize;
34
use serde_json::{ json, from_str };
45
use std::collections::HashMap;
@@ -83,3 +84,24 @@ pub fn http_call(url: &str, options: Option<HttpCallOptions>) -> Result<CallResu
8384
result: parsed_result.result,
8485
});
8586
}
87+
88+
pub enum Outcome {
89+
Invalid,
90+
Valid(String)
91+
}
92+
93+
pub fn exit_with_outcome(outcome: &Outcome) {
94+
let result = match outcome {
95+
Outcome::Invalid => json!({ "type": "Invalid" }).to_string(),
96+
Outcome::Valid(answer) => json!({ "type": "Valid", "value": answer }).to_string(),
97+
};
98+
99+
let exit_code = match outcome {
100+
Outcome::Invalid => 1,
101+
Outcome::Valid(_) => 0,
102+
};
103+
104+
// Make sure we don't interfene with other logs by adding \n
105+
print!("\n{}", result);
106+
process::exit(exit_code);
107+
}
Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,85 @@
11
use std::env;
2-
use std::collections::HashMap;
2+
use serde::{ Deserialize, Serialize };
33
use jsonpath_rust::JsonPathFinder;
4+
use bigdecimal::{ BigDecimal };
5+
use std::str::{ FromStr };
6+
use std::ops::{ Add, Div, Mul };
47

58
mod flux;
69

10+
#[derive(Serialize, Deserialize, Debug)]
11+
struct Source {
12+
end_point: String,
13+
source_path: String,
14+
}
15+
716
fn main() {
817
let args: Vec<String> = env::args().collect();
9-
let mut headers: HashMap<String, String> = HashMap::new();
18+
let sources: Vec<Source> = serde_json::from_str(args.get(1).unwrap()).unwrap();
19+
let sources_type = args.get(2).unwrap();
20+
let mut string_result: String = "".to_string();
21+
let mut number_result = BigDecimal::from(0);
22+
let mut used_sources: u32 = 0;
23+
24+
assert_ne!(sources.len(), 0, "ERR_NO_SOURCES");
25+
26+
if sources_type == "string" {
27+
assert_eq!(sources.len(), 1, "ERR_TOO_MUCH_SOURCES");
28+
} else if sources_type == "number" {
29+
assert!(args.get(3).is_some(), "ERR_NO_MULTIPLIER");
30+
} else {
31+
panic!("ERR_UNSUPPORTED_TYPE");
32+
}
33+
34+
for source in sources.iter() {
35+
let http_result = flux::http_call(&source.end_point, None);
36+
37+
if http_result.is_err() {
38+
eprintln!("{}", http_result.unwrap_err().error);
39+
continue;
40+
}
41+
42+
let http_result_data = http_result.unwrap().result;
43+
44+
// Finds a value in the returned json using the path given in args
45+
let finder = JsonPathFinder::from_str(&http_result_data, &source.source_path).unwrap();
46+
let found_values = finder.find();
47+
let result_value = found_values.get(0).unwrap();
48+
49+
if sources_type == "string" {
50+
string_result = result_value.as_str().unwrap().to_string();
51+
} else if sources_type == "number" {
52+
// Converting numbers to strings so we can use BigDecimal to combine them all
53+
let mut val: Option<BigDecimal> = None;
54+
55+
if result_value.is_i64() {
56+
val = Some(BigDecimal::from(result_value.as_i64().unwrap()));
57+
} else if result_value.is_string() {
58+
// strings can still be valid numbers
59+
val = Some(BigDecimal::from_str(result_value.as_str().unwrap()).unwrap());
60+
} else if result_value.is_u64() {
61+
val = Some(BigDecimal::from(result_value.as_u64().unwrap()));
62+
} else if result_value.is_f64() {
63+
val = Some(BigDecimal::from_str(&result_value.as_f64().unwrap().to_string()).unwrap());
64+
}
65+
66+
number_result = number_result.add(val.unwrap());
67+
}
1068

11-
headers.insert("Content-Type".to_string(), "json".to_string());
69+
used_sources += 1;
70+
}
1271

13-
let http_result = flux::http_call(args.get(1).unwrap(), Some(flux::HttpCallOptions {
14-
headers: Some(headers),
15-
body: None,
16-
method: Some("GET".to_string()),
17-
}));
72+
assert_ne!(used_sources, 0, "ERR_FAILING_SOURCES");
1873

19-
let response = match http_result {
20-
Ok(data) => data,
21-
Err(error) => panic!("Status: {}, Error: {}", error.status, error.error),
22-
};
74+
if sources_type == "string" {
75+
flux::exit_with_outcome(&flux::Outcome::Valid(string_result));
76+
} else if sources_type == "number" {
77+
let multiplier = BigDecimal::from_str(args.get(3).unwrap()).unwrap();
2378

24-
let finder = JsonPathFinder::from_str(&response.result, args.get(2).unwrap());
25-
let unwrapped_finder = finder.unwrap();
26-
let finder_values = unwrapped_finder.find();
27-
let final_value = finder_values.get(0).unwrap();
79+
number_result = number_result.div(BigDecimal::from(used_sources));
80+
number_result = number_result.mul(multiplier);
81+
number_result = number_result.round(0);
2882

29-
print!("{}", final_value.as_str().unwrap());
83+
flux::exit_with_outcome(&flux::Outcome::Valid(number_result.to_string()));
84+
}
3085
}

src/Process.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { URL as NodeURL } from "url";
55
import { extractMessageFromEvent } from './services/WorkerService';
66
import { MessageType, RequestMessage } from './models/WorkerMessage';
77
import ProcessMethods from './ProcessMethods';
8+
import { ExecuteResult } from './models/ExecuteResult';
89

910
export default class Process extends EventEmitter {
1011
public context: Context;
@@ -44,4 +45,16 @@ export default class Process extends EventEmitter {
4445

4546
worker.postMessage(spawnMessage);
4647
}
48+
}
49+
50+
export function execute(context: Context): Promise<ExecuteResult> {
51+
return new Promise((resolve) => {
52+
const process = new Process(context);
53+
54+
process.on('exit', (result: ExecuteResult) => {
55+
resolve(result);
56+
});
57+
58+
process.spawn();
59+
});
4760
}

src/services/VirtualMachineService.ts

Lines changed: 54 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,52 +18,62 @@ export function prepareWasmBinary(binary: Uint8Array): Uint8Array {
1818
return meteredWasm;
1919
}
2020

21-
export async function executeWasm(context: VmContext): Promise<ExecuteResult> {
22-
const wasmFs = new WasmFs();
23-
const virtualFs = new VirtualFs(wasmFs);
24-
25-
try {
26-
const random = alea(context.randomSeed);
27-
const wasi = new WASI({
28-
args: context.args,
29-
env: context.env,
30-
bindings: {
31-
...WASIBindings,
32-
fs: virtualFs.getFs(),
33-
hrtime: () => BigInt(context.timestamp),
34-
randomFillSync: (buffer: any, offset, size) => {
35-
const randomBuffer = generateFixedBuffer(random.int32().toString(), size ?? buffer.length - offset);
36-
buffer.set(randomBuffer, offset);
37-
return buffer;
21+
export function executeWasm(context: VmContext): Promise<ExecuteResult> {
22+
return new Promise(async (resolve) => {
23+
const wasmFs = new WasmFs();
24+
const virtualFs = new VirtualFs(wasmFs);
25+
26+
try {
27+
const random = alea(context.randomSeed);
28+
const wasi = new WASI({
29+
args: context.args,
30+
env: context.env,
31+
bindings: {
32+
...WASIBindings,
33+
fs: virtualFs.getFs(),
34+
hrtime: () => BigInt(context.timestamp),
35+
randomFillSync: (buffer: any, offset, size) => {
36+
const randomBuffer = generateFixedBuffer(random.int32().toString(), size ?? buffer.length - offset);
37+
buffer.set(randomBuffer, offset);
38+
return buffer;
39+
},
40+
exit: (rval: number) => {
41+
resolve({
42+
code: rval,
43+
gasUsed: context.gasUsed.toString(),
44+
logs: virtualFs.getLogOutput(),
45+
});
46+
},
3847
},
39-
},
40-
});
41-
42-
const preparedBinary = prepareWasmBinary(context.binary);
43-
const module = await WebAssembly.compile(preparedBinary.buffer);
44-
const wasiImports = wasi.getImports(module);
45-
const wasmImports = new WasmImports(context, wasiImports);
46-
const instance = await WebAssembly.instantiate(module, wasmImports.getImports());
47-
48-
wasmImports.setMemory(instance.exports.memory as WebAssembly.Memory);
49-
wasi.start(instance);
48+
});
49+
50+
const preparedBinary = prepareWasmBinary(context.binary);
51+
const module = await WebAssembly.compile(preparedBinary.buffer);
52+
const wasiImports = wasi.getImports(module);
53+
const wasmImports = new WasmImports(context, wasiImports);
54+
const instance = await WebAssembly.instantiate(module, wasmImports.getImports());
55+
56+
wasmImports.setMemory(instance.exports.memory as WebAssembly.Memory);
57+
wasi.start(instance);
58+
59+
resolve({
60+
code: 0,
61+
logs: virtualFs.getLogOutput(),
62+
gasUsed: context.gasUsed.toString(),
63+
});
64+
} catch(error: any) {
65+
const logs = virtualFs.getLogOutput();
5066

51-
const logs = virtualFs.getLogOutput();
67+
// Runtime errors should be handled by logs
68+
if (!(error instanceof WebAssembly.RuntimeError)) {
69+
logs.push(error?.message);
70+
}
5271

53-
return {
54-
code: 0,
55-
logs,
56-
gasUsed: context.gasUsed.toString(),
72+
resolve({
73+
code: error?.code ?? 1,
74+
logs,
75+
gasUsed: context.gasUsed.toString(),
76+
});
5777
}
58-
} catch(error: any) {
59-
const logs = virtualFs.getLogOutput();
60-
61-
logs.push(error?.message);
62-
63-
return {
64-
code: error?.code ?? 1,
65-
logs,
66-
gasUsed: context.gasUsed.toString(),
67-
};
68-
}
78+
});
6979
}

0 commit comments

Comments
 (0)