hevy 0.1.0

An async Rust client library for the Hevy public API (https://api.hevyapp.com/docs/)
Documentation
# hevy

An async Rust client library for the [Hevy API](https://api.hevyapp.com/docs/) — the public REST API for the [Hevy](https://hevy.com) workout tracking app.

Hevy's public API is only available to **Hevy Pro** users. Grab an API key from your [developer settings](https://hevy.com/settings?developer) before using this crate.

> Per Hevy's own docs: "we make no guarantees that we won't completely change the structure or abandon the project entirely, so use it at your own risk." This crate follows the documented `0.0.1` OpenAPI spec as closely as possible and deserializes leniently where the docs are inconsistent.

## Features

- Full coverage of the documented API surface:
  - **Workouts** — list, get, create, update, count, and sync via events
  - **Routines** — list, get, create, update
  - **Routine folders** — list, get, create
  - **Exercise templates** — list, get, create custom exercises
  - **Exercise history** — fetch historical sets for an exercise, with optional date filtering
  - **Body measurements** — list, get, create, update
  - **User info** — fetch the authenticated user's profile
- Fully async, built on [`reqwest`]https://docs.rs/reqwest + [`tokio`]https://docs.rs/tokio
- Typed request/response models with `serde`
- Ergonomic builders for constructing workouts and routines
- Structured error type ([`HevyError`]) distinguishing transport errors, decode errors, and API errors (with status code + message)

## Installation

```sh
cargo add hevy
```

Or add it to your `Cargo.toml` manually:

```toml
[dependencies]
hevy = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```

## Usage

```rust,no_run
use hevy::{CreateWorkoutRequest, HevyClient, NewWorkout, WorkoutExerciseInput, WorkoutSetInput};

#[tokio::main]
async fn main() -> hevy::Result<()> {
    let client = HevyClient::new("YOUR_API_KEY");

    // List workouts.
    let page = client.list_workouts(Some(1), Some(5)).await?;
    for workout in &page.workouts {
        println!("{}: {}", workout.id, workout.title);
    }

    // Log a new workout.
    let workout = NewWorkout::new(
        "Friday Leg Day",
        "2024-08-14T12:00:00Z",
        "2024-08-14T12:30:00Z",
    )
    .with_exercise(
        WorkoutExerciseInput::new("D04AC939").with_set(WorkoutSetInput::normal(100.0, 10)),
    );
    let created = client.create_workout(&CreateWorkoutRequest::new(workout)).await?;
    println!("Created workout {}", created.id);

    Ok(())
}
```

See [`examples/basic_usage.rs`](examples/basic_usage.rs) for a more complete tour (fetching your profile, listing workouts, and logging a new one). Run it with:

```sh
HEVY_API_KEY=your-key cargo run --example basic_usage
```

## Error handling

Every fallible method returns [`hevy::Result<T>`], where errors are represented by [`hevy::HevyError`]:

- `HevyError::Request` — the HTTP request itself failed (network, TLS, timeout, etc.)
- `HevyError::Decode` — the response body couldn't be parsed into the expected type
- `HevyError::Api { status, message }` — the API responded with a non-2xx status; `message` is extracted from the response's `error` field when present
- `HevyError::Config` — the client was misconfigured

## Testing

The test suite uses [`wiremock`](https://docs.rs/wiremock) to mock the Hevy API over HTTP, so no real API key or network access is required:

```sh
cargo test
```

## License

MIT