CI / test (push) Canceled after 0s
Covers Clients, Invoices, Quotes, Credits, Payments, Products, Expenses, Projects, Tasks, and Vendors, each with list, list_all, get, create, update, and delete, generated by the resource_methods! macro. Every model carries a #[serde(flatten)] extra map so fields the API returns but the crate does not model survive a get/update round-trip instead of being silently dropped. Includes a Nix dev shell (Rust via rust-overlay), a GitHub Actions CI workflow running build/test/fmt/clippy, and round-trip tests for the extra-preservation invariant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
73 lines
3.0 KiB
Markdown
73 lines
3.0 KiB
Markdown
# 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 <test_name> # 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<T>` / `ItemResponse<T>` 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<String, Value>`
|
|
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`.
|