200 lines
5.9 KiB
Markdown
200 lines
5.9 KiB
Markdown
# Agent Guidelines for Jello
|
|
|
|
This document provides guidelines for AI coding agents working on the Jello codebase.
|
|
|
|
## Project Overview
|
|
|
|
Jello is a WIP video client for Jellyfin written in Rust, focusing on HDR video playback using:
|
|
- **iced** - Primary GUI toolkit
|
|
- **gstreamer** - Video + audio decoding library
|
|
- **wgpu** - Rendering video from GStreamer in iced
|
|
|
|
## Build, Test, and Lint Commands
|
|
|
|
### Building
|
|
```bash
|
|
# Build in release mode
|
|
cargo build --release
|
|
cargo build -r
|
|
|
|
# Build specific workspace member
|
|
cargo build -p api
|
|
cargo build -p gst
|
|
cargo build -p ui-iced
|
|
|
|
# Run the application
|
|
cargo run --release -- -vv
|
|
just jello # Uses justfile
|
|
```
|
|
|
|
### Testing
|
|
```bash
|
|
# Run all tests in workspace
|
|
cargo test --workspace
|
|
|
|
# Run tests for a specific package
|
|
cargo test -p gst
|
|
cargo test -p api
|
|
cargo test -p iced-video
|
|
|
|
# Run a single test by name
|
|
cargo test test_appsink
|
|
cargo test -p gst test_appsink
|
|
|
|
# Run a specific test in a specific file
|
|
cargo test -p gst --test <test_file_name> <test_function_name>
|
|
|
|
# Run tests with output
|
|
cargo test -- --nocapture
|
|
cargo test -- --show-output
|
|
```
|
|
|
|
### Linting and Formatting
|
|
```bash
|
|
# Check code without building
|
|
cargo check
|
|
cargo check --workspace
|
|
|
|
# Run clippy (linter)
|
|
cargo clippy
|
|
cargo clippy --workspace
|
|
cargo clippy --workspace -- -D warnings
|
|
|
|
# Format code
|
|
cargo fmt
|
|
cargo fmt --all
|
|
|
|
# Check formatting without modifying files
|
|
cargo fmt --all -- --check
|
|
```
|
|
|
|
### Other Tools
|
|
```bash
|
|
# Check for security vulnerabilities and license compliance
|
|
cargo deny check
|
|
|
|
# Generate Jellyfin type definitions
|
|
just typegen
|
|
```
|
|
|
|
## Code Style Guidelines
|
|
|
|
### Rust Edition
|
|
- Use **Rust 2024 edition** (as specified in Cargo.toml files)
|
|
|
|
### Imports
|
|
- Use `use` statements at the top of files
|
|
- Group imports: std library, external crates, then local modules
|
|
- Use `crate::` for absolute paths within the crate
|
|
- Common pattern: create a `priv_prelude` module for internal imports
|
|
- Use `pub use` to re-export commonly used items
|
|
- Use wildcard imports (`use crate::priv_prelude::*;`) within internal modules when a prelude exists
|
|
|
|
Example:
|
|
```rust
|
|
use std::sync::Arc;
|
|
|
|
use reqwest::{Method, header::InvalidHeaderValue};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::errors::*;
|
|
```
|
|
|
|
### Naming Conventions
|
|
- **Types/Structs/Enums**: PascalCase (e.g., `JellyfinClient`, `Error`, `AppSink`)
|
|
- **Functions/Methods**: snake_case (e.g., `request_builder`, `stream_url`)
|
|
- **Variables**: snake_case (e.g., `access_token`, `device_id`)
|
|
- **Constants**: SCREAMING_SNAKE_CASE (e.g., `NEXT_ID`, `GST`)
|
|
- **Modules**: snake_case (e.g., `priv_prelude`, `error_stack`)
|
|
|
|
### Error Handling
|
|
- Use **`error-stack`** for error handling with context propagation
|
|
- Use **`thiserror`** for defining error types
|
|
- Standard error type pattern:
|
|
```rust
|
|
pub use error_stack::{Report, ResultExt};
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
#[error("An error occurred")]
|
|
pub struct Error;
|
|
|
|
pub type Result<T, E = error_stack::Report<Error>> = core::result::Result<T, E>;
|
|
```
|
|
- Attach context to errors using `.change_context(Error)` and `.attach("description")`
|
|
- Use `#[track_caller]` on functions that may panic or error for better error messages
|
|
- Error handling example:
|
|
```rust
|
|
self.inner
|
|
.set_state(gstreamer::State::Playing)
|
|
.change_context(Error)
|
|
.attach("Failed to set pipeline to Playing state")?;
|
|
```
|
|
|
|
### Types
|
|
- Prefer explicit types over type inference when it improves clarity
|
|
- Use `impl Trait` for function parameters when appropriate (e.g., `impl AsRef<str>`)
|
|
- Use `Option<T>` and `Result<T, E>` idiomatically
|
|
- Use `Arc<T>` for shared ownership
|
|
- Use newtype patterns for semantic clarity (e.g., `ApiKey` wrapping `secrecy::SecretBox<String>`)
|
|
|
|
### Formatting
|
|
- Use 4 spaces for indentation
|
|
- Line length: aim for 100 characters, but not strictly enforced
|
|
- Use trailing commas in multi-line collections
|
|
- Follow standard Rust formatting conventions (enforced by `cargo fmt`)
|
|
|
|
### Documentation
|
|
- Add doc comments (`///`) for public APIs
|
|
- Use inline comments (`//`) sparingly, prefer self-documenting code
|
|
- Include examples in doc comments when helpful
|
|
|
|
### Async/Await
|
|
- Use `tokio` as the async runtime
|
|
- Mark async functions with `async` keyword
|
|
- Use `.await` for async operations
|
|
- Common pattern: `tokio::fs` for file operations
|
|
|
|
### Module Structure
|
|
- Use `mod.rs` or inline modules as appropriate
|
|
- Keep related functionality together
|
|
- Use `pub(crate)` for internal APIs
|
|
- Re-export commonly used items at crate root
|
|
|
|
### Macros
|
|
- Custom macros used: `wrap_gst!`, `parent_child!`
|
|
- Use macros for reducing boilerplate, only in the `gst` crate
|
|
|
|
### Testing
|
|
- Place tests in the same file with `#[test]` or `#[cfg(test)]`
|
|
- Use descriptive test function names (e.g., `test_appsink`, `unique_generates_different_ids`)
|
|
- Initialize tracing in tests when needed for debugging
|
|
|
|
### Dependencies
|
|
- Prefer well-maintained crates from crates.io
|
|
- Use `workspace.dependencies` for shared dependencies across workspace members
|
|
- Pin versions when stability is important
|
|
|
|
### Workspace Structure
|
|
The project uses a Cargo workspace with multiple members:
|
|
- `.` - Main jello binary
|
|
- `api` - Jellyfin API client
|
|
- `gst` - GStreamer wrapper
|
|
- `ui-iced` - Iced UI implementation
|
|
- `ui-gpui` - GPUI UI implementation (optional)
|
|
- `store` - Secret/data/storage management
|
|
- `jello-types` - Shared type definitions
|
|
- `typegen` - Jellyfin type generator
|
|
- `crates/iced-video` - Custom iced video widget
|
|
- `examples/hdr-gstreamer-wgpu` - HDR example
|
|
|
|
### Project-Specific Patterns
|
|
- Use `LazyLock` for global initialization (e.g., GStreamer init)
|
|
- Use the builder pattern with method chaining (e.g., `request_builder()`)
|
|
- Use `tap` crate's `.pipe()` for functional transformations
|
|
- Prefer `BTreeMap`/`BTreeSet` over `HashMap`/`HashSet` when order matters
|
|
- Prefer a functional programming style instead of an imperative one.
|
|
- When building UIs keep the handler and view code in the same module (eg. settings view and settings handle in the same file)
|
|
|
|
## License
|
|
All code in this project is MIT licensed.
|