Files
monorepo/CLAUDE.md
Savanni D'Gerinel fdb253bce1
Some checks failed
Monorepo build / build-flake (push) Has been cancelled
Rename the PWA url to SPA
2026-03-18 12:56:54 -04:00

3.7 KiB

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

# 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=... SPA_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:

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:

// 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!:

/// 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:

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