5.9 KiB
5.9 KiB
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
# 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
# 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
# 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
# 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
usestatements 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_preludemodule for internal imports - Use
pub useto re-export commonly used items - Use wildcard imports (
use crate::priv_prelude::*;) within internal modules when a prelude exists
Example:
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-stackfor error handling with context propagation - Use
thiserrorfor defining error types - Standard error type pattern:
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:
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 Traitfor function parameters when appropriate (e.g.,impl AsRef<str>) - Use
Option<T>andResult<T, E>idiomatically - Use
Arc<T>for shared ownership - Use newtype patterns for semantic clarity (e.g.,
ApiKeywrappingsecrecy::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
tokioas the async runtime - Mark async functions with
asynckeyword - Use
.awaitfor async operations - Common pattern:
tokio::fsfor file operations
Module Structure
- Use
mod.rsor 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
gstcrate
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.dependenciesfor shared dependencies across workspace members - Pin versions when stability is important
Workspace Structure
The project uses a Cargo workspace with multiple members:
.- Main jello binaryapi- Jellyfin API clientgst- GStreamer wrapperui-iced- Iced UI implementationui-gpui- GPUI UI implementation (optional)store- Secret/data/storage managementjello-types- Shared type definitionstypegen- Jellyfin type generatorcrates/iced-video- Custom iced video widgetexamples/hdr-gstreamer-wgpu- HDR example
Project-Specific Patterns
- Use
LazyLockfor global initialization (e.g., GStreamer init) - Use the builder pattern with method chaining (e.g.,
request_builder()) - Use
tapcrate's.pipe()for functional transformations - Prefer
BTreeMap/BTreeSetoverHashMap/HashSetwhen 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.