diff --git a/flake.lock b/flake.lock
index 3ea16a5..8afe7dd 100644
--- a/flake.lock
+++ b/flake.lock
@@ -6,11 +6,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
- "lastModified": 1744618085,
- "narHash": "sha256-+VdhZsIiIDtyOL88c4U/Os1PsCMLOCyScIeWL4hxJRM=",
+ "lastModified": 1752821162,
+ "narHash": "sha256-mwQHDPD8DTQW7LIV7werk1TUVh7Wg/IuYeuTArlEqlo=",
"owner": "nix-community",
"repo": "fenix",
- "rev": "a85d390a5607188dca2dbc39b5b37571651d69ce",
+ "rev": "934ce7f8a4dbf849f334b8c311be5d5d97a1af69",
"type": "github"
},
"original": {
@@ -39,11 +39,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1744463964,
- "narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=",
+ "lastModified": 1752687322,
+ "narHash": "sha256-RKwfXA4OZROjBTQAl9WOZQFm7L8Bo93FQwSJpAiSRvo=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650",
+ "rev": "6e987485eb2c77e5dcc5af4e3c70843711ef9251",
"type": "github"
},
"original": {
@@ -55,11 +55,11 @@
},
"nixpkgs_2": {
"locked": {
- "lastModified": 1744463964,
- "narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=",
+ "lastModified": 1752687322,
+ "narHash": "sha256-RKwfXA4OZROjBTQAl9WOZQFm7L8Bo93FQwSJpAiSRvo=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650",
+ "rev": "6e987485eb2c77e5dcc5af4e3c70843711ef9251",
"type": "github"
},
"original": {
@@ -79,11 +79,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
- "lastModified": 1744539868,
- "narHash": "sha256-NPUnfDAwLD69aKetxjC7lV5ysrvs1IKC0Sy4Zai10Mw=",
+ "lastModified": 1752743757,
+ "narHash": "sha256-bsiX5DRwYMsaiBykptwJac36AlWTh5ZaHUPHYf6XiPY=",
"owner": "rust-lang",
"repo": "rust-analyzer",
- "rev": "8365cf853e791c93fa8bc924f031f11949bb1a3c",
+ "rev": "f73ce3c8a897be1ae5cf77332288e331824435e5",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index 97b9197..d15a75d 100644
--- a/flake.nix
+++ b/flake.nix
@@ -18,12 +18,14 @@
system:
let
pkgs = import nixpkgs { inherit system; };
- toolchain = fenix.packages.${system}.stable.toolchain;
+ # toolchain = fenix.packages.${system}.stable.toolchain;
+ nightlyToolchain = fenix.packages.${system}.latest.toolchain;
in
{
devShells.default = pkgs.mkShell {
packages = [
- toolchain
+ # toolchain
+ nightlyToolchain
];
};
}
diff --git a/torn-api/Cargo.toml b/torn-api/Cargo.toml
index 61870c5..b0b0a14 100644
--- a/torn-api/Cargo.toml
+++ b/torn-api/Cargo.toml
@@ -7,13 +7,17 @@ license-file = { workspace = true }
repository = { workspace = true }
homepage = { workspace = true }
+[hints]
+mostly-unused = true
+
[features]
-default = ["scopes", "requests", "builder", "models"]
+default = ["scopes", "requests", "builder", "models", "reqwest"]
scopes = ["builder"]
builder = ["requests", "dep:bon"]
requests = ["models"]
models = ["dep:serde_repr"]
strum = ["dep:strum"]
+reqwest = ["dep:reqwest"]
[dependencies]
serde = { workspace = true, features = ["derive"] }
@@ -22,7 +26,7 @@ serde_json = { workspace = true }
bon = { version = "3.6", optional = true }
bytes = "1"
http = "1"
-reqwest = { version = "0.12", default-features = false, features = [
+reqwest = { version = "0.12", default-features = false, optional = true, features = [
"rustls-tls",
"json",
"brotli",
diff --git a/torn-api/README.md b/torn-api/README.md
new file mode 100644
index 0000000..8196c12
--- /dev/null
+++ b/torn-api/README.md
@@ -0,0 +1,101 @@
+
torn-api.rs
+
+
+ Rust Torn API bindings
+
+
+
+
+
+
+
+
+
+Async and typesafe bindings for the [Torn API](https://www.torn.com/swagger.php) that are auto-generated based on the v2 OpenAPI spec.
+
+## Installation
+torn-api requires an async runtime such as [tokio](https://github.com/tokio-rs/tokio) or [smol](https://github.com/smol-rs/smol) in order to function. It *should* be fully runtime agnostic when the `reqwest` feature is disabled.
+```toml
+[dependencies]
+torn-api = "1.7"
+```
+
+### Features
+- `reqwest`: Include an implementation of the client which uses the [reqwest](https://github.com/seanmonstar/reqwest) crate as its HTTP client. Requires tokio runtime.
+- `models`: Generate response and parameter model definitions.
+- `requests`: Generate requests model definitions.
+- `scopes`: Generate scope objects which group endpoints by category.
+- `builder`: Generate builders using [bon](https://github.com/elastio/bon) for all request structs.
+- `strum`: Derive [EnumIs](https://docs.rs/strum/latest/strum/derive.EnumIs.html) and [EnumTryAs](https://docs.rs/strum/latest/strum/derive.EnumTryAs.html) for all auto-generated enums.
+
+## Quickstart
+
+```rust
+use torn_api::{executor::{ReqwestClient, ExecutorExt}, models::RacingRaceTypeEnum};
+
+let client = ReqwestClient::new("XXXXXXXXXXXXX");
+
+let response = client.user().races(|r| r.cat(RacingRaceTypeEnum::Official)).await.unwrap();
+
+let race = &response.races[0];
+
+println!("Race '{}': winner was {}", race.title, race.results[0].driver_id);
+```
+
+### Use with undocumented endpoints
+The v2 API exposes v1 endpoints as undocumented endpoints in cases where they haven't been ported over yet. It is still possible (though not recommended) to use this crate with such endpoints by manually implementing the [`IntoRequest`](https://docs.rs/torn-api/latest/torn_api/request/trait.IntoRequest.html) trait.
+
+
+```rust
+use torn_api::{
+ executor::{ReqwestClient, Executor},
+ models::UserId,
+ request::{IntoRequest, ApiRequest}
+};
+
+#[derive(serde::Deserialize)]
+struct UserBasic {
+ id: UserId,
+ name: String,
+ level: i32
+}
+
+struct UserBasicRequest(UserId);
+
+impl IntoRequest for UserBasicRequest {
+ type Discriminant = UserId;
+ type Response = UserBasic;
+ fn into_request(self) -> (Self::Discriminant, ApiRequest) {
+ let request = ApiRequest {
+ path: format!("/user/{}/basic", self.0),
+ parameters: Vec::default(),
+ };
+
+ (self.0, request)
+ }
+}
+
+let client = ReqwestClient::new("XXXXXXXXXXXXX");
+let basic = client.fetch(UserBasicRequest(UserId(1))).await.unwrap();
+```
+
+### Implementing your own API executor
+If you don't wish to use reqwest, or want to use custom logic for which API to use, you have to implement the [`Executor`](https://docs.rs/torn-api/latest/torn_api/executor/trait.Executor.html) trait for your custom executor.
+
+## Safety
+The crate is compiled with `#![forbid(unsafe_code)]`.
+
+## Warnings
+- ⚠️ The Torn v2 API, on which this wrapper is based, is under active development and changes frequently. No guarantees are made that this wrapper always matches the latest version of the API.
+- ⚠️ This crate contains a lot of macro-heavy, auto-generated code. If you experience slow compile times, you may want try testing the [nightly only `-Zhint-mostly-unused` option](https://blog.rust-lang.org/inside-rust/2025/07/15/call-for-testing-hint-mostly-unused) to see if improvements in compile time apply to your use case.
diff --git a/torn-api/src/executor.rs b/torn-api/src/executor.rs
index 61ba949..2ca1f33 100644
--- a/torn-api/src/executor.rs
+++ b/torn-api/src/executor.rs
@@ -1,22 +1,25 @@
use std::future::Future;
use futures::{Stream, StreamExt};
+#[cfg(feature = "reqwest")]
use http::{header::AUTHORIZATION, HeaderMap, HeaderValue};
use serde::Deserialize;
+#[cfg(feature = "reqwest")]
+use crate::request::ApiRequest;
+use crate::request::{ApiResponse, IntoRequest};
#[cfg(feature = "scopes")]
use crate::scopes::{
- BulkFactionScope, BulkForumScope, BulkMarketScope, BulkRacingScope, BulkTornScope,
- BulkUserScope, FactionScope, ForumScope, MarketScope, RacingScope, TornScope, UserScope,
-};
-use crate::{
- request::{ApiRequest, ApiResponse, IntoRequest},
- scopes::{BulkKeyScope, KeyScope},
+ BulkFactionScope, BulkForumScope, BulkKeyScope, BulkMarketScope, BulkRacingScope,
+ BulkTornScope, BulkUserScope, FactionScope, ForumScope, KeyScope, MarketScope, RacingScope,
+ TornScope, UserScope,
};
+/// Central trait of the crate that is used to execute api requests.
pub trait Executor: Sized {
type Error: From + From + Send;
+ /// Execute an api request.
fn execute(
self,
request: R,
@@ -24,12 +27,13 @@ pub trait Executor: Sized {
where
R: IntoRequest;
+ /// Execute a request and deserialise the associated response type.
fn fetch(self, request: R) -> impl Future