[package]
name          = "exchange-apiws"
version       = "0.9.0"
edition       = "2024"
rust-version  = "1.94.1"
authors       = ["nuniesmith"]
license       = "MIT"
repository    = "https://github.com/nuniesmith/exchange-apiws"
description   = "Exchange REST and WebSocket clients — spot trading, futures, account management, and live data streams"
documentation = "https://docs.rs/exchange-apiws"
readme        = "README.md"
keywords      = ["kucoin", "crypto", "trading", "websocket", "futures"]
categories    = ["api-bindings", "network-programming", "asynchronous"]

# Keep the published crate lean — internal planning notes, dev tooling, and
# generated lint reports aren't useful to downstream consumers. `src/`,
# `tests/`, `examples/`, README, CHANGELOG, and LICENSE still ship. `bots/` is
# a standalone companion crate (see bots/kucoin-dip-buyer), not library code.
exclude = ["todo.md", "scripts/", "lint_report.md", "lint_report_bin", "/.github", "bots/"]

# docs.rs builds with every exchange enabled so the optional connectors
# appear in the published docs; `docsrs` gates nightly-only doc features.
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[features]
# Default-features enable all exchanges so existing users see no change.
# Downstream crates that only need a subset can opt out via
# `default-features = false` and re-enable the ones they want:
#
#     exchange-apiws = { version = "0.2", default-features = false, features = ["kucoin", "binance"] }
#
# KuCoin is the original implementation and is always on — most of its
# code lives in the crate's shared modules (auth, client, ws/runner) and
# can't be cleanly gated without splitting the crate.
default    = ["binance", "bybit", "kraken", "cryptocom", "coinbase", "okx"]
binance    = []
bybit      = []
kraken     = []
cryptocom  = []
coinbase   = []
okx        = []

# Expose development-only helpers (e.g. `Credentials::sim()`) in non-test
# binaries. Never enable this in production builds.
testutils = []

# ── Runtime dependencies ───────────────────────────────────────────────────────

[dependencies]
# Async runtime — minimal surface: time (sleep/interval), macros (tokio::main, select!),
# and rt-multi-thread for the default multi-core scheduler.
tokio = { version = "1.51.1", features = ["rt-multi-thread", "macros", "time"] }

# HTTP — use rustls so the crate builds on musl/Alpine without OpenSSL.
# Disable default-features to drop the default OpenSSL backend, then enable the
# rustls backend explicitly. `rustls-no-provider` brings in the rustls TLS stack
# plus `rustls-platform-verifier` for CA roots WITHOUT a crypto provider — so we
# avoid `aws-lc-rs` (needs cmake + clang, breaks slim/musl Docker builds). The
# `ring` provider is supplied by the direct `rustls` dep below and installed at
# runtime by `tls::ensure_crypto_provider`.
reqwest = { version = "0.13.2", default-features = false, features = ["json", "rustls-no-provider"] }

# rustls crypto provider for reqwest's `rustls-no-provider` backend. Pin to the
# pure-Rust `ring` provider (NOT aws-lc-rs) so the crate compiles with no C
# toolchain; `tls::ensure_crypto_provider` installs it as the process default.
rustls = { version = "0.23", default-features = false, features = ["ring"] }

# WebSocket — rustls-tls-native-roots uses the OS trust store so wss:// works out of the box.
tokio-tungstenite = { version = "0.29.0", features = ["rustls-tls-native-roots"] }

# Streaming combinators (SinkExt / StreamExt) needed for the WS loop.
futures-util = { version = "0.3.32", default-features = false, features = ["sink", "std"] }

# Serialisation
serde      = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"

# Error handling
thiserror = "2.0.18"
anyhow    = "1.0.102"

# Structured logging — zero-cost when no subscriber is installed.
tracing = "0.1.44"

# Time — serde feature lets Chrono types round-trip through JSON.
chrono = { version = "=0.4.45", features = ["serde"] }

# Unique client-order IDs.
uuid = { version = "1.23.0", features = ["v4"] }

# Zero credential memory on drop — prevents key material leaking in heap dumps.
zeroize = { version = "1", features = ["derive"] }

# KuCoin HMAC-SHA256 signing
hmac   = "0.13"
sha2   = "0.11"
base64 = "0.22"

# ── Dev / test dependencies ────────────────────────────────────────────────────

[dev-dependencies]
# Integration tests and examples need a runtime. `signal` powers the
# ctrl-c handler in `examples/kucoin_supervised_feed`.
tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal"] }
# Print tracing output during `cargo test -- --nocapture`.
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# HTTP mock server for integration tests — spins up a local server per test.
wiremock = "0.6"

# ── Library entry point ────────────────────────────────────────────────────────

[lib]
# Must match the crate name with hyphens replaced by underscores.
# Previously incorrectly set to "indicators" (copy/paste from another project).
name = "exchange_apiws"
path = "src/lib.rs"

# ── Examples (gated by exchange feature) ──────────────────────────────────────

[[example]]
name = "binance_public_market"
required-features = ["binance"]

[[example]]
name = "bybit_public_market"
required-features = ["bybit"]

[[example]]
name = "bybit_private_trading"
required-features = ["bybit"]

[[example]]
name = "kraken_public_market"
required-features = ["kraken"]

[[example]]
name = "cryptocom_public_market"
required-features = ["cryptocom"]

[[example]]
name = "multi_exchange_aggregator"
required-features = ["binance", "bybit"]

# kucoin_supervised_feed has no required-features — KuCoin is always on.
[[example]]
name = "kucoin_supervised_feed"

# ── Build profiles ─────────────────────────────────────────────────────────────

[profile.dev]
opt-level = 0
debug     = true

[profile.release]
opt-level     = 3
lto           = "thin"
codegen-units = 16
strip         = true
debug         = false
panic         = "abort"

[profile.release-with-debug]
inherits = "release"
debug    = true
strip    = false

[profile.ci]
inherits      = "release"
lto           = false
codegen-units = 256

# ── Lint policy ────────────────────────────────────────────────────────────────

[lints.rust]
unsafe_code  = "warn"
missing_docs = "warn"   # promote to "warn" — crates.io users expect rustdoc

[lints.clippy]
all      = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
nursery  = { level = "warn", priority = -1 }

# ── Allowed — documented rationale ────────────────────────────────────────────

# KuCoin returns numbers as strings; error paths are self-documenting.
missing_errors_doc  = "allow"
missing_panics_doc  = "allow"

# New in clippy 1.96 — suggests Duration::from_mins / from_hours, which were
# stabilised AFTER this crate's declared rust-version (1.94.1). Taking the
# suggestion would break the MSRV build, so keep the explicit-unit form.
duration_suboptimal_units = "allow"

# `KuCoinClient::get`, `KuCoinClient::post` etc. — repetition is intentional.
module_name_repetitions = "allow"

# Builders / constructors are almost always worth calling for side effects.
must_use_candidate = "allow"

# Numeric conversions: period/size values are always well within f64 range.
cast_precision_loss      = "allow"
cast_possible_truncation = "allow"
cast_sign_loss           = "allow"
cast_lossless            = "allow"

# Multi-field request bodies; splitting harms readability.
too_many_arguments = "allow"

# if-let is clearer than map_or for complex bodies.
option_if_let_else = "allow"

# Domain terms (KC-API-KEY, GTC, IOC) are not code identifiers.
doc_markdown = "allow"

# Credentials passed by value into constructor; ref complicates call sites.
needless_pass_by_value = "allow"
