Skip to content

Commit a6ab62f

Browse files
claudeHerklos
authored andcommitted
Add Rust regression test suite and safe accessor implementations
- Add comprehensive regression test suite (56 tests) in rust/tests/regression.rs covering safe accessor methods, Value helpers, describe() shape validation, fixture-based comparison, and live network tests - Add manually-maintained safe accessor implementations to Exchange trait: safe_string, safe_integer, safe_float, safe_value, safe_bool, safe_dict, safe_list, safe_number, parse_number, safe_timestamp and all _2/_n variants - Add free helper functions: safe_get, value_to_string_opt, value_to_i64_opt, value_to_f64_opt - Update rustTranspiler.ts with MANUALLY_IMPLEMENTED_METHODS set to skip generating stubs for methods already implemented in the Exchange trait - Add test fixtures in rust/tests/fixtures/ for JS-parity validation - Regenerate all exchange .rs files (safe accessor stubs removed) https://claude.ai/code/session_01KE37uEYUSbY3Ug9yFSjpzZ
1 parent 155cfc8 commit a6ab62f

236 files changed

Lines changed: 437286 additions & 22 deletions

File tree

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: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,25 @@ const __dirname = path.dirname(fileURLToPath(metaFileUrl));
2525
const FUNCTION_INFO: Record<string, Record<string, { paramsCount: number; async: boolean }>> = {};
2626
const RUST_STUB_MODE = true;
2727

28+
// Methods that are manually implemented in the Exchange trait (in exchange.rs)
29+
// and should NOT be auto-generated as stubs by the transpiler.
30+
const MANUALLY_IMPLEMENTED_METHODS = new Set([
31+
'safeString', 'safeString2', 'safeStringN',
32+
'safeStringLower', 'safeStringLower2', 'safeStringLowerN',
33+
'safeStringUpper', 'safeStringUpper2', 'safeStringUpperN',
34+
'safeInteger', 'safeInteger2', 'safeIntegerN',
35+
'safeIntegerProduct', 'safeIntegerProduct2', 'safeIntegerProductN',
36+
'safeTimestamp', 'safeTimestamp2', 'safeTimestampN',
37+
'safeFloat', 'safeFloat2', 'safeFloatN',
38+
'safeValue', 'safeValue2', 'safeValueN',
39+
'parseNumber', 'parseToInt', 'parseToNumeric',
40+
'safeNumber', 'safeNumber2', 'safeNumberN', 'safeNumberOmitZero',
41+
'safeIntegerOmitZero',
42+
'safeBool', 'safeBool2', 'safeBoolN',
43+
'safeDict', 'safeDict2', 'safeDictN',
44+
'safeList', 'safeList2', 'safeListN',
45+
]);
46+
2847
function isUpperCase(x: string) {
2948
return x && x.length > 0 && x[0] === x.toUpperCase()[0];
3049
}
@@ -342,6 +361,10 @@ function transpileMethodToRust(opts: {
342361
return '';
343362
}
344363
const fname = fnNode.id?.name || 'unknown';
364+
// Skip methods that are manually implemented above the auto-generated delimiter.
365+
if (MANUALLY_IMPLEMENTED_METHODS.has(fname)) {
366+
return '';
367+
}
345368
let isSelfImmutable = false;
346369
if (
347370
fname.startsWith('safe') ||

examples/rust/benchmark.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// AUTO-GENERATED: transpiled from TypeScript examples/
2+
// Source: examples/ts/benchmark.ts
3+
4+
#[tokio::main]
5+
async fn main() {
6+
println!("No exchange constructor detected in benchmark.ts; generated placeholder.");
7+
}

examples/rust/build_ohlcv_bars.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// AUTO-GENERATED: transpiled from TypeScript examples/
2+
// Source: examples/ts/build-ohlcv-bars.ts
3+
4+
use ccxt::exchange::{normalize, Value};
5+
use ccxt::pro::binance::{Binance, BinanceImpl};
6+
use serde_json::json;
7+
8+
#[tokio::main]
9+
async fn main() {
10+
let mut exchange = BinanceImpl::new(Value::Json(json!({})));
11+
let symbol: Value = "OGN/USDT".into();
12+
13+
let rv = Binance::build_ohlcvc(&mut exchange, symbol.clone(), Value::Undefined, Value::Undefined, Value::Undefined);
14+
println!("buildOHLCVC: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
15+
let rv = Binance::fetch_trades(&mut exchange, symbol.clone(), Value::Undefined, Value::Undefined, Value::Undefined).await;
16+
println!("fetchTrades: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
17+
let rv = Binance::filter_by_since_limit(&mut exchange, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined);
18+
println!("filterBySinceLimit: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
19+
// skipped: milliseconds (not found in transpiled trait)
20+
let rv = Binance::watch_trades(&mut exchange, symbol.clone(), Value::Undefined, Value::Undefined, Value::Undefined).await;
21+
println!("watchTrades: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
22+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// AUTO-GENERATED: transpiled from TypeScript examples/
2+
// Source: examples/ts/compare-two-exchanges-capabilities.ts
3+
4+
use ccxt::exchanges::okx::{OkxImpl};
5+
use ccxt::exchange::Value;
6+
use serde_json::json;
7+
8+
#[tokio::main]
9+
async fn main() {
10+
let _exchange = OkxImpl::new(Value::Json(json!({})));
11+
println!("No exchange method calls detected in compare-two-exchanges-capabilities.ts; generated placeholder.");
12+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// AUTO-GENERATED: transpiled from TypeScript examples/
2+
// Source: examples/ts/create-order-position-with-takeprofit-stoploss.ts
3+
4+
use ccxt::exchange::{normalize, Value};
5+
use ccxt::exchanges::okx::{Okx, OkxImpl};
6+
use serde_json::json;
7+
8+
#[tokio::main]
9+
async fn main() {
10+
let mut exchange = OkxImpl::new(Value::Json(json!({})));
11+
let symbol: Value = "DOGE/USDT:USDT".into();
12+
13+
// skipped: cancel_order (not found in transpiled trait)
14+
let rv = Okx::create_order(&mut exchange, symbol.clone(), Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined).await;
15+
println!("createOrder: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
16+
let rv = Okx::fetch_open_orders(&mut exchange, symbol.clone(), Value::Undefined, Value::Undefined, Value::Undefined).await;
17+
println!("fetchOpenOrders: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
18+
let rv = Okx::fetch_ticker(&mut exchange, symbol.clone(), Value::Undefined).await;
19+
println!("fetchTicker: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
20+
let rv = Okx::load_markets(&mut exchange, Value::Undefined, Value::Undefined).await;
21+
println!("loadMarkets: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
22+
let rv = Okx::market(&mut exchange, Value::Undefined);
23+
println!("market: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
24+
let rv = Okx::price_to_precision(&mut exchange, Value::Undefined, Value::Undefined);
25+
println!("priceToPrecision: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
26+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// AUTO-GENERATED: transpiled from TypeScript examples/
2+
// Source: examples/ts/create-order-ws-example.ts
3+
4+
use ccxt::exchange::{normalize, Value};
5+
use ccxt::pro::binance::{Binance, BinanceImpl};
6+
use serde_json::json;
7+
8+
#[tokio::main]
9+
async fn main() {
10+
let mut exchange = BinanceImpl::new(Value::Json(json!({})));
11+
let symbol: Value = "ETH/USDT".into();
12+
13+
let rv = Binance::create_order_ws(&mut exchange, symbol.clone(), Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined).await;
14+
println!("createOrderWs: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
15+
let rv = Binance::load_markets(&mut exchange, Value::Undefined, Value::Undefined).await;
16+
println!("loadMarkets: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
17+
let rv = Binance::set_sandbox_mode(&mut exchange, Value::Undefined);
18+
println!("setSandboxMode: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// AUTO-GENERATED: transpiled from TypeScript examples/
2+
// Source: examples/ts/create-orders-example.ts
3+
4+
use ccxt::exchange::{normalize, Value};
5+
use ccxt::exchanges::binance::{Binance, BinanceImpl};
6+
use serde_json::json;
7+
8+
#[tokio::main]
9+
async fn main() {
10+
let mut exchange = BinanceImpl::new(Value::Json(json!({})));
11+
let symbol: Value = "BTC/USDT".into();
12+
13+
let rv = Binance::create_orders(&mut exchange, symbol.clone(), Value::Undefined).await;
14+
println!("createOrders: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
15+
let rv = Binance::load_markets(&mut exchange, Value::Undefined, Value::Undefined).await;
16+
println!("loadMarkets: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
17+
let rv = Binance::set_sandbox_mode(&mut exchange, Value::Undefined);
18+
println!("setSandboxMode: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
19+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// AUTO-GENERATED: transpiled from TypeScript examples/
2+
// Source: examples/ts/create-trailing-amount-order.ts
3+
4+
use ccxt::exchange::{normalize, Value};
5+
use ccxt::exchanges::bingx::{Bingx, BingxImpl};
6+
use serde_json::json;
7+
8+
#[tokio::main]
9+
async fn main() {
10+
let mut exchange = BingxImpl::new(Value::Json(json!({})));
11+
let symbol: Value = "BTC/USDT:USDT".into();
12+
13+
let rv = Bingx::create_order(&mut exchange, symbol.clone(), Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined).await;
14+
println!("createOrder: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
15+
let rv = Bingx::create_trailing_amount_order(&mut exchange, symbol.clone(), Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined).await;
16+
println!("createTrailingAmountOrder: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
17+
let rv = Bingx::load_markets(&mut exchange, Value::Undefined, Value::Undefined).await;
18+
println!("loadMarkets: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
19+
let rv = Bingx::set_sandbox_mode(&mut exchange, Value::Undefined);
20+
println!("setSandboxMode: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// AUTO-GENERATED: transpiled from TypeScript examples/
2+
// Source: examples/ts/create-trailing-percent-order.ts
3+
4+
use ccxt::exchange::{normalize, Value};
5+
use ccxt::exchanges::bingx::{Bingx, BingxImpl};
6+
use serde_json::json;
7+
8+
#[tokio::main]
9+
async fn main() {
10+
let mut exchange = BingxImpl::new(Value::Json(json!({})));
11+
let symbol: Value = "BTC/USDT:USDT".into();
12+
13+
let rv = Bingx::create_order(&mut exchange, symbol.clone(), Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined).await;
14+
println!("createOrder: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
15+
let rv = Bingx::create_trailing_percent_order(&mut exchange, symbol.clone(), Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined, Value::Undefined).await;
16+
println!("createTrailingPercentOrder: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
17+
let rv = Bingx::load_markets(&mut exchange, Value::Undefined, Value::Undefined).await;
18+
println!("loadMarkets: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
19+
let rv = Bingx::set_sandbox_mode(&mut exchange, Value::Undefined);
20+
println!("setSandboxMode: {}", normalize(&rv).map(|v| v.to_string()).unwrap_or_else(|| "undefined".into()));
21+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// AUTO-GENERATED: transpiled from TypeScript examples/
2+
// Source: examples/ts/custom-proxy-agent-for-js.ts
3+
4+
use ccxt::exchanges::kraken::{KrakenImpl};
5+
use ccxt::exchange::Value;
6+
use serde_json::json;
7+
8+
#[tokio::main]
9+
async fn main() {
10+
let _exchange = KrakenImpl::new(Value::Json(json!({})));
11+
println!("No exchange method calls detected in custom-proxy-agent-for-js.ts; generated placeholder.");
12+
}

0 commit comments

Comments
 (0)