Skip to content

Commit a1f1e2c

Browse files
Herklosclaude
andcommitted
Remove RUST_STUB_MODE: generate real transpiled exchange code
- Remove RUST_STUB_MODE flag entirely from rustTranspiler.ts - Exchange-specific methods now get fully transpiled JS→Rust bodies instead of `{ Value::Undefined }` stubs - Base Exchange class methods still use stubs (runtime not ready) - Inherited base methods removed from exchange traits (available via Exchange supertrait) to avoid E0034 ambiguity - Fix TryStatement to emit try block inline (catch as comment) - Add AST visitors: SpreadElement, ArrowFunction, FunctionExpression, TemplateLiteral, ForOf/ForIn, SwitchStatement - Fix dispatch call matching (case-insensitive API method names) - Fix method call generation: use UFCS for overrides, instance calls for inherited methods - Add BoolExt trait, From<i32>, From<Value> for serde_json::Value - Add Precise static methods, Math methods, Array::is_array - Add runtime stub methods for pre-delimiter Exchange.js functions (load_markets, omit, iso8601, etc.) - Fix argument count table for safe* methods and other utilities - Make Exchange require ValueTrait supertrait for self.get() access Result: binance.rs goes from 499 stub methods to 0 stubs, with 202 real transpiled functions across 13,477 lines. ~55 type-level compilation errors remain (async patterns, iterator conflicts) that need incremental transpiler refinements. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 90b45bc commit a1f1e2c

81 files changed

Lines changed: 121352 additions & 160210 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build/rustTranspiler.ts

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,37 @@ function getArgumentCount(className: string, node: any) {
301301
stringDiv: 3,
302302
throttle: 1,
303303
totp: 1,
304+
// safe* methods have 3 params (dictionary, key, defaultValue) but are
305+
// often called with 2 in JS (default is undefined)
306+
safeString: 3, safeString2: 4, safeStringN: 3,
307+
safeStringLower: 3, safeStringLower2: 4, safeStringLowerN: 3,
308+
safeStringUpper: 3, safeStringUpper2: 4, safeStringUpperN: 3,
309+
safeInteger: 3, safeInteger2: 4, safeIntegerN: 3,
310+
safeIntegerProduct: 3, safeIntegerProduct2: 4, safeIntegerProductN: 3,
311+
safeTimestamp: 3, safeTimestamp2: 4, safeTimestampN: 3,
312+
safeFloat: 3, safeFloat2: 4, safeFloatN: 3,
313+
safeValue: 3, safeValue2: 4, safeValueN: 3,
314+
safeNumber: 3, safeNumber2: 4, safeNumberN: 3,
315+
safeBool: 3, safeBool2: 4, safeBoolN: 3,
316+
safeDict: 3, safeDict2: 4, safeDictN: 3,
317+
safeList: 3, safeList2: 4, safeListN: 3,
318+
safeNumberOmitZero: 3, safeIntegerOmitZero: 3,
319+
// Other methods with optional params
320+
loadMarkets: 2, omit: 2, iso8601: 1, milliseconds: 0,
321+
numberToString: 1, inArray: 2, decimalToPrecision: 5,
322+
sortBy: 4, indexBy: 2, filterBy: 3, groupBy: 2,
323+
filterByLimit: 4, filterBySinceLimit: 5,
324+
parseBidAsk: 3, parseBidsAsks: 3, parseOrderBook: 5,
325+
parseNumber: 2, parseToInt: 2,
326+
json: 1, encode: 1, urlencode: 1, rawencode: 1,
327+
hash: 3, hmac: 4, jwt: 4,
328+
encodeURIComponent: 1, uuid22: 0, yymmdd: 2,
329+
precisionFromString: 1, createSafeDictionary: 0,
330+
sign: 6, handleErrors: 9,
331+
throwExactlyMatchedException: 3, throwBroadlyMatchedException: 3,
332+
parseJson: 1, arrayConcat: 2,
333+
urlencodeWithArrayRepeat: 1, parse8601: 1,
334+
safeMarket: 4, safeCurrency: 2, safeCurrencyCode: 2, safeSymbol: 4,
304335
};
305336
const fname = getCalleeFunctionName(node);
306337
const rv = FUNCTION_INFO[className]?.[fname] || FUNCTION_INFO['Exchange']?.[fname];
@@ -708,6 +739,10 @@ function transpileMethodToRust(opts: {
708739
emit(' '.repeat(state.indentLevel * state.indentSize));
709740
};
710741

742+
// Build a lowercase set for case-insensitive dispatch matching
743+
// (enumerateApiMethodMapping generates slightly different casing than JS source)
744+
const apiMethodsLower = new Set([...apiMethods].map(m => m.toLowerCase()));
745+
711746
const isDispatchCall = (node: any) => {
712747
if (node.type !== 'CallExpression') {
713748
throw new Error('Unexpected node type');
@@ -716,7 +751,7 @@ function transpileMethodToRust(opts: {
716751
node.callee.type === 'MemberExpression' &&
717752
node.callee.object.type === 'ThisExpression' &&
718753
node.callee.property.type === 'Identifier' &&
719-
((apiMethods.has(node.callee.property.name) && !node.callee.computed) ||
754+
((apiMethodsLower.has(node.callee.property.name.toLowerCase()) && !node.callee.computed) ||
720755
(node.callee.property.name === 'method' && node.callee.computed))
721756
);
722757
};
@@ -2142,7 +2177,7 @@ class RustTranspiler {
21422177
'use std::str::FromStr;',
21432178
'use serde::{Deserialize, Serialize};',
21442179
'use serde_json::json;',
2145-
'use crate::exchange::{Exchange, ExchangeImpl, Precise, Value, ValueTrait, JSON, Array, Object, Math, parse_int, shift_2, extend_2, normalize};',
2180+
'use crate::exchange::{Exchange, ExchangeImpl, Precise, Value, ValueTrait, BoolExt, JSON, Array, Object, Math, parse_int, shift_2, extend_2, normalize};',
21462181
'',
21472182
'use crate::exchange::{PRECISE_BASE, TRUNCATE, ROUND, ROUND_UP, ROUND_DOWN};',
21482183
'use crate::exchange::{DECIMAL_PLACES, SIGNIFICANT_DIGITS, TICK_SIZE, NO_PADDING, PAD_WITH_ZERO};',

rust/src/exchange.rs

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,15 @@ use std::collections::BTreeMap;
1414
use std::str::FromStr;
1515

1616
pub type JSON = serde_json::Value;
17-
pub type Array = Vec<Value>;
17+
pub struct Array;
18+
impl Array {
19+
pub fn is_array(v: Value) -> Value {
20+
match &v {
21+
Value::Json(serde_json::Value::Array(_)) => true.into(),
22+
_ => false.into(),
23+
}
24+
}
25+
}
1826
pub type Object = BTreeMap<String, Value>;
1927

2028
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -460,16 +468,6 @@ impl Value {
460468

461469
pub fn array_concat(a: Value, b: Value) -> Value { a.concat(b) }
462470

463-
pub fn to_array(v: Value) -> Value {
464-
match &v {
465-
Value::Json(JSON::Array(_)) => v,
466-
_ => Value::new_array(),
467-
}
468-
}
469-
470-
pub fn is_array(v: &Value) -> bool {
471-
matches!(v, Value::Json(JSON::Array(_)))
472-
}
473471
}
474472

475473
impl From<i64> for Value {
@@ -478,6 +476,21 @@ impl From<i64> for Value {
478476
}
479477
}
480478

479+
impl From<i32> for Value {
480+
fn from(v: i32) -> Self {
481+
Value::Json(json!(v as i64))
482+
}
483+
}
484+
485+
impl From<Value> for serde_json::Value {
486+
fn from(v: Value) -> serde_json::Value {
487+
match v {
488+
Value::Json(j) => j,
489+
Value::Undefined => serde_json::Value::Null,
490+
}
491+
}
492+
}
493+
481494
impl From<usize> for Value {
482495
fn from(v: usize) -> Self {
483496
Value::Json(json!(v))
@@ -632,6 +645,14 @@ impl PartialOrd for Value {
632645
}
633646
}
634647

648+
pub trait BoolExt {
649+
fn is_truthy(&self) -> bool;
650+
}
651+
652+
impl BoolExt for bool {
653+
fn is_truthy(&self) -> bool { *self }
654+
}
655+
635656
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
636657
pub struct Precise;
637658

@@ -644,6 +665,7 @@ impl Precise {
644665
pub fn string_abs(a: Value) -> Value { a }
645666
pub fn string_neg(a: Value) -> Value { a.neg() }
646667
pub fn string_eq(a: Value, b: Value) -> bool { a == b }
668+
pub fn string_equals(a: Value, b: Value) -> bool { a == b }
647669
pub fn string_gt(a: Value, b: Value) -> bool { a > b }
648670
pub fn string_ge(a: Value, b: Value) -> bool { a >= b }
649671
pub fn string_lt(a: Value, b: Value) -> bool { a < b }
@@ -654,6 +676,17 @@ impl Precise {
654676

655677
pub struct Math;
656678

679+
impl Math {
680+
pub fn min(a: Value, b: Value) -> Value { if a <= b { a } else { b } }
681+
pub fn max(a: Value, b: Value) -> Value { if a >= b { a } else { b } }
682+
pub fn floor(a: Value) -> Value { a }
683+
pub fn ceil(a: Value) -> Value { a }
684+
pub fn round(a: Value) -> Value { a }
685+
pub fn abs(a: Value) -> Value { a }
686+
pub fn pow(a: Value, _b: Value) -> Value { a }
687+
pub fn log(a: Value) -> Value { a }
688+
}
689+
657690
pub fn parse_int(_value: Value, _radix: Value) -> Value {
658691
match _value {
659692
Value::Json(JSON::Number(n)) => Value::from(n.as_i64().unwrap_or(0)),
@@ -1323,6 +1356,41 @@ pub trait Exchange: ValueTrait {
13231356
result
13241357
}
13251358

1359+
// ---------------------------------------------------------------------------
1360+
// Runtime methods — defined before the delimiter in Exchange.js, so the
1361+
// transpiler doesn't see them. Stub implementations here; replace with
1362+
// real logic as the runtime grows.
1363+
// ---------------------------------------------------------------------------
1364+
1365+
async fn load_markets(&mut self, _reload: Value, _params: Value) -> Value { Value::Undefined }
1366+
fn omit(&self, obj: Value, _keys: Value) -> Value { obj }
1367+
fn omit_zero(&self, obj: Value) -> Value { obj }
1368+
fn iso8601(&self, _ts: Value) -> Value { Value::Undefined }
1369+
fn parse8601(&self, _s: Value) -> Value { Value::Undefined }
1370+
fn milliseconds(&self) -> Value { Value::Undefined }
1371+
fn number_to_string(&self, v: Value) -> Value { v.to_string() }
1372+
fn in_array(&self, _needle: Value, _haystack: Value) -> bool { false }
1373+
fn decimal_to_precision(&self, v: Value, _r: Value, _p: Value, _c: Value, _pad: Value) -> Value { v }
1374+
fn uuid22(&self) -> Value { Value::from("") }
1375+
fn yymmdd(&self, _ts: Value, _sep: Value) -> Value { Value::from("") }
1376+
fn urlencode(&self, _params: Value) -> Value { Value::from("") }
1377+
fn urlencode_with_array_repeat(&self, _params: Value) -> Value { Value::from("") }
1378+
fn rawencode(&self, _params: Value) -> Value { Value::from("") }
1379+
fn encode_uri_component(&self, s: Value) -> Value { s }
1380+
fn encode(&self, s: Value) -> Value { s }
1381+
fn sort_by(&self, arr: Value, _key: Value, _desc: Value, _dir: Value) -> Value { arr }
1382+
fn index_by(&self, arr: Value, _key: Value) -> Value { arr }
1383+
fn filter_by(&self, arr: Value, _key: Value, _value: Value) -> Value { arr }
1384+
fn group_by(&self, arr: Value, _key: Value) -> Value { arr }
1385+
fn precision_from_string(&self, _s: Value) -> Value { Value::Undefined }
1386+
fn json(&self, v: Value) -> Value { v }
1387+
fn create_safe_dictionary(&self) -> Value { Value::new_object() }
1388+
fn parse_timeframe(&self, _tf: Value) -> Value { Value::Undefined }
1389+
fn hash(&self, _msg: Value, _hash: Value, _enc: Value) -> Value { Value::from("") }
1390+
fn hmac(&self, _msg: Value, _sec: Value, _hash: Value, _enc: Value) -> Value { Value::from("") }
1391+
fn array_concat(&self, a: Value, b: Value) -> Value { a.concat(b) }
1392+
fn sum(&self, a: Value, b: Value) -> Value { a + b }
1393+
13261394
// ---------------------------------------------------------------------------
13271395
// METHODS BELOW THIS LINE ARE TRANSPILED FROM JAVASCRIPT
13281396
fn describe(&self) -> Value { Value::Undefined }

0 commit comments

Comments
 (0)