# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What this is An async Rust client for the [Invoice Ninja](https://www.invoiceninja.com/) v5 API. ## Development environment This project uses Nix for toolchain management (Rust via `rust-overlay`): ```sh nix develop ``` ## Common commands ```sh cargo build cargo test cargo test # run a single test cargo check cargo clippy cargo fmt # Run the example (requires IN_API_TOKEN, optionally IN_BASE_URL) IN_API_TOKEN=... cargo run --example list_clients ``` Tests live in `tests/` (integration-style, no network access required); `src/` has no `#[test]` modules. ## Architecture - `src/client.rs` — `InvoiceNinjaClient` holds a `reqwest::Client` (with the `X-Api-Token` auth header baked in) and a `base_url`. Low-level helpers (`get_list`, `get_one`, `post_one`, `put_one`, `delete_one`) handle request dispatch and decode `ListResponse` / `ItemResponse` envelopes, mapping non-2xx responses through `api_error` into `Error::Api`. - Per-resource methods (`list_clients`, `get_invoice`, `create_product`, etc.) are generated by the `resource_methods!` macro at the bottom of `client.rs`, which expands to `list`/`list_all`/`get`/`create`/`update`/`delete` for a given REST path and model type. **To add a new resource**: add a model under `src/models/`, re-export it from `src/models/mod.rs`, and add one `resource_methods!` invocation in `client.rs`. Note the macro takes six method names before the path and model — adding a resource means passing all six. - `list_*` returns one page; `list_all_*` goes through `get_all_pages`, which follows `page`/`per_page` until a page returns fewer rows than requested. - `src/models/` — one file per resource (`client.rs`, `invoice.rs`, `payment.rs`, `product.rs`, `expense.rs`, `project.rs`, `task.rs`, `vendor.rs`), plus `common.rs` for shared envelope/pagination types (`ListParams`, `ListResponse`, `ItemResponse`, `Meta`, `Pagination`). Invoice Ninja stores quotes and credits in the same table as invoices (an `entity_type` field distinguishes them), so `Quote` and `Credit` are type aliases for `Invoice` (`src/models/invoice.rs`) and share the `/quotes`, `/credits`, and `/invoices` paths via the macro. - Every model struct has a `#[serde(flatten)] pub extra: Map` field. This preserves any API fields not explicitly modeled, so a `get` followed by an `update` round-trips without silently dropping data. Keep this pattern when adding fields or new models. - `src/error.rs` — `Error` enum (`thiserror`) covering transport, JSON decode, API error responses (with per-field validation `errors` map), and client construction errors. `ApiErrorBody` mirrors Invoice Ninja's error JSON shape. ## Coverage Clients, Invoices, Quotes, Credits, Payments, Products, Expenses, Projects, Tasks, and Vendors, each with `list`, `list_all`, `get`, `create`, `update`, and `delete`.