Two-crate workspace: immo-core for pure financial logic (wasm-compatible, no async/IO deps) and immo-web for HTTP and rendering. Both crates are still cargo-new skeletons; no domain code yet. Pins the toolchain to 1.97 with rustfmt, clippy and the wasm32-unknown-unknown target so the core crate's wasm constraint is checkable locally. Cargo.lock is committed since the workspace ships a binary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
94 lines
4.4 KiB
Markdown
94 lines
4.4 KiB
Markdown
# CLAUDE.md
|
|
|
|
## Project
|
|
|
|
Web app that answers one question with data: for a given property in France, is buying
|
|
financially better than renting and investing the same cash? It outputs an amortisation
|
|
schedule, total ownership costs, and breakeven curves under several market hypotheses.
|
|
|
|
This is a financial tool. A wrong number is worse than a slow response, an ugly page, or a
|
|
missing feature. Correctness and traceable sources come first.
|
|
|
|
I am learning Rust through this project. See "Working with me" below.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
cargo run -p immo-web # start the server on :3000
|
|
cargo watch -x 'run -p immo-web' # live reload during development
|
|
cargo test --workspace # all tests
|
|
cargo test -p immo-core # domain tests only, fast
|
|
cargo fmt --all
|
|
cargo clippy --workspace --all-targets -- -D warnings
|
|
```
|
|
|
|
Run `cargo clippy` and `cargo test -p immo-core` before telling me a change is finished.
|
|
|
|
## Architecture boundary
|
|
|
|
Two crates. The boundary is a rule, not a description.
|
|
|
|
`crates/immo-core` holds all financial logic as pure functions over plain structs.
|
|
|
|
- No `tokio`, no `axum`, no `reqwest`, no filesystem, no network access.
|
|
- Must stay compilable to `wasm32-unknown-unknown`. After adding any dependency, verify with
|
|
`cargo check -p immo-core --target wasm32-unknown-unknown`.
|
|
- Time is a parameter. Never call `Utc::now()` or read the system clock inside `immo-core`.
|
|
|
|
`crates/immo-web` holds HTTP, HTML rendering, and input parsing. It contains no financial
|
|
arithmetic.
|
|
|
|
If a computation is tempting to write inline in a handler, it belongs in `immo-core`.
|
|
|
|
## Stack, pinned
|
|
|
|
- axum 0.8. Path parameters use `{param}`, **not** `:param` — that is 0.7 syntax and will not compile.
|
|
- `#[async_trait]` is not needed on `FromRequest` / `FromRequestParts` in 0.8. Do not add it.
|
|
- askama for templates, tower-http for static files and compression, serde, rust_decimal.
|
|
- Server-rendered HTML. No npm, no bundler, no JS framework. Charts are a CDN `<script>` tag fed
|
|
by a JSON endpoint.
|
|
|
|
Before writing axum code from memory, check the version in `Cargo.toml` and verify the API against
|
|
docs.rs for that exact version.
|
|
|
|
## Domain rules
|
|
|
|
- Money is `rust_decimal::Decimal` or integer cents. Never `f64`. Never `as` casts between numeric
|
|
types in financial code.
|
|
- Rates are stored as annual nominal and converted explicitly. Name variables so the period is
|
|
unambiguous (`taeg_annual`, `monthly_rate`), never a bare `rate`.
|
|
- Keep French domain vocabulary in type and field names: `FraisDeNotaire`, `TaxeFonciere`,
|
|
`IndiceReferenceLoyers`, `LoyerDeReferenceMajore`, `ZoneTendue`, `Taeg`. Do not translate them.
|
|
- Every hardcoded assumption lives in `crates/immo-core/src/assumptions.rs` with a `///` comment
|
|
giving its source and the date it was checked. Nothing hardcoded anywhere else.
|
|
- If you do not know a real figure — a barème, a tax rate, a fee schedule, an IRL value — say so and
|
|
leave a clearly named placeholder. Do not invent plausible-looking French tax numbers.
|
|
- TAEG and taux nominal are different things. Distinguish them explicitly wherever both appear.
|
|
|
|
## Error handling and tests
|
|
|
|
- `thiserror` for typed errors in `immo-core`; `anyhow` only at the `immo-web` boundary.
|
|
- No `unwrap()`, `expect()`, or `panic!()` outside tests and `main`.
|
|
- Every `core` function producing a number the user sees has a known-answer test, with the worked
|
|
example stated in the test name or a comment above it.
|
|
- Test the amortisation schedule against hand-checked values, never against the implementation's
|
|
own output.
|
|
|
|
## Working with me
|
|
|
|
I know traits, `Result`, and the borrow checker. I do not know the ecosystem well, and learning it
|
|
is half the point of this project.
|
|
|
|
- When you use a non-obvious idiom — extractors, `Layer`, lifetimes on a struct, `impl Trait` in
|
|
return position — explain why in one or two sentences, in chat rather than as a code comment.
|
|
- If my request implies an unidiomatic design, say so before implementing it.
|
|
- Prefer the simple version over the clever one, and tell me when you deliberately chose the
|
|
simple one.
|
|
|
|
## Do not
|
|
|
|
- Add a dependency without asking first, including small ones.
|
|
- Introduce npm, a bundler, TypeScript, or a frontend framework.
|
|
- Write financial logic in `crates/immo-web`.
|
|
- Leave commented-out code, or comments that restate what the code does.
|
|
- Reformat or restructure files you were not asked to touch.
|