Add instructions for Claude
This commit is contained in:
122
CLAUDE.md
Normal file
122
CLAUDE.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a Rust monorepo. The primary active project is **Visions**, a tabletop RPG game management application with WebSocket-based real-time communication. It uses:
|
||||
- **Server**: Axum web framework with SQLite (rusqlite)
|
||||
- **UI**: Yew (WebAssembly) with yew-router
|
||||
- **Shared types**: visions-types crate shared between server and UI
|
||||
- **Component library**: glimmer-yew provides reusable Yew components
|
||||
|
||||
## Build Commands
|
||||
|
||||
```bash
|
||||
# Build server
|
||||
cargo build -p visions-server
|
||||
|
||||
# Build UI (requires trunk)
|
||||
cd visions/ui && trunk build
|
||||
|
||||
# Run server (requires env vars)
|
||||
DATABASE_PATH=... RELYING_PARTY=... RELYING_PARTY_ORIGIN=... PWA_BASE_URL=... cargo run -p visions-server
|
||||
|
||||
# Run all tests
|
||||
cargo test
|
||||
|
||||
# Run specific test
|
||||
cargo test -p visions-core test_name
|
||||
|
||||
# Run integration tests for server
|
||||
cargo test -p visions-server --test integrations
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Visions Crates
|
||||
- **visions/core**: Business logic, database layer, App struct. Uses nested `Result<Result<T, Error>, Fatal>` pattern for error handling.
|
||||
- **visions/server**: Axum HTTP/WebSocket handlers and routing. Handlers use `http_adapter` to convert nested results to HTTP responses.
|
||||
- **visions/types**: Shared types between server and UI. Uses `identifier!` macro to generate ID types (UserId, GameId, etc.).
|
||||
- **visions/ui**: Yew WebAssembly frontend. Uses `StateProvider` for app state, `StyleProvider` for theming.
|
||||
|
||||
### glimmer-yew
|
||||
Reusable Yew component library with styling via `stylist`. Components use `Stylesheet` context for theming. Key exports in `prelude` module.
|
||||
|
||||
## Rust Style Guidelines
|
||||
|
||||
### Error Handling
|
||||
Use nested results to separate recoverable errors from fatal errors:
|
||||
```rust
|
||||
fn fallible() -> Result<Result<(), Error>, Fatal>
|
||||
fn search() -> Result<Option<()>, Fatal>
|
||||
```
|
||||
Fatal errors should never be caught except at the top level. Recoverable errors should never be promoted to fatal.
|
||||
|
||||
### Let/Else Bindings
|
||||
Prefer let/else over match for unwrapping:
|
||||
```rust
|
||||
// Preferred
|
||||
let Some(game) = db.game(&image.game)? else {
|
||||
return Ok(Err(Error::NotFound(image.game.as_str().to_string())));
|
||||
};
|
||||
|
||||
// Avoid
|
||||
let game = match db.game(&image.game)? {
|
||||
Some(game) => game,
|
||||
None => return Ok(Err(Error::NotFound(image.game.as_str().to_string()))),
|
||||
};
|
||||
```
|
||||
|
||||
### Test Style
|
||||
Tests use SCENARIO/GIVEN/WHEN/THEN comments and `cool_asserts::assert_matches!`:
|
||||
```rust
|
||||
/// SCENARIO: Description
|
||||
/// GIVEN: Preconditions
|
||||
/// WHEN: Action
|
||||
/// THEN: Expected outcome
|
||||
#[tokio::test]
|
||||
async fn test_name() {
|
||||
// GIVEN: ...
|
||||
let support = TestSupport::new().await;
|
||||
|
||||
// WHEN: ...
|
||||
let result = support.app.some_action().await;
|
||||
|
||||
// THEN: ...
|
||||
assert_matches!(result, Ok(Ok(value)) => {
|
||||
assert_eq!(value.field, expected);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Yew Component Guidelines (glimmer-yew)
|
||||
|
||||
### Naming
|
||||
- Name components naturally (e.g., `Button`, not `ButtonComponent`)
|
||||
- Properties struct: `<ComponentName>Props`
|
||||
|
||||
### Properties
|
||||
- Use `AttrValue` for string properties
|
||||
- Use `Prop<T>` wrapper for sizable non-string properties
|
||||
- Always accept `#[prop_or_default] pub class: Classes`
|
||||
- Callbacks: `on_<action>` with `#[prop_or(Callback::from(|_| {}))]`
|
||||
- Optional properties: `#[prop_or_default]`
|
||||
|
||||
### Cloning in Callbacks
|
||||
Use the `clone!` macro from glimmer-yew:
|
||||
```rust
|
||||
use glimmer_yew::clone;
|
||||
|
||||
let on_click = Callback::from(clone!(state, move |_| {
|
||||
state.set(new_value);
|
||||
}));
|
||||
|
||||
// Multiple values
|
||||
clone!((state, props), move |_| { ... })
|
||||
```
|
||||
|
||||
### Styling
|
||||
- Components should have no margin
|
||||
- Avoid CSS nesting except for pseudo-selectors
|
||||
- Use `stylist::css!` macro with stylesheet tokens from context
|
||||
Reference in New Issue
Block a user