mirror of https://github.com/buster-so/buster.git
195 lines
5.5 KiB
Plaintext
195 lines
5.5 KiB
Plaintext
|
---
|
||
|
description: This is helpul docs for buildng hanlders in the project.
|
||
|
globs: ibs/handlers/**/*.rs
|
||
|
alwaysApply: false
|
||
|
---
|
||
|
# Handler Rules and Best Practices
|
||
|
|
||
|
## Overview
|
||
|
Handlers are the core business logic components that implement functionality used by both REST and WebSocket endpoints. They are also often used by the CLI package. This document outlines the structure, patterns, and best practices for working with handlers.
|
||
|
|
||
|
## File Structure
|
||
|
- `libs/handlers/src/`
|
||
|
- `[domain]/` - Domain-specific modules (e.g., messages, chats, files, metrics)
|
||
|
- `mod.rs` - Re-exports handlers and types
|
||
|
- `types.rs` - Domain-specific data structures
|
||
|
- `*_handler.rs` - Individual handler implementations
|
||
|
- `helpers/` - Helper functions and utilities for handlers
|
||
|
|
||
|
## Naming Conventions
|
||
|
- Handler files should be named with the pattern: `[action]_[resource]_handler.rs`
|
||
|
- Example: `get_chat_handler.rs`, `delete_message_handler.rs`
|
||
|
- Handler functions should follow the same pattern: `[action]_[resource]_handler`
|
||
|
- Example: `get_chat_handler()`, `delete_message_handler()`
|
||
|
- Type definitions should be clear and descriptive
|
||
|
- Request types: `[Action][Resource]Request`
|
||
|
- Response types: `[Action][Resource]Response`
|
||
|
|
||
|
## Handler Implementation Guidelines
|
||
|
|
||
|
### Function Signatures
|
||
|
```rust
|
||
|
pub async fn action_resource_handler(
|
||
|
// Parameters typically include:
|
||
|
request: ActionResourceRequest, // For REST/WS request data
|
||
|
user: User, // For authenticated user context
|
||
|
// Other contextual parameters as needed
|
||
|
) -> Result<ActionResourceResponse> {
|
||
|
// Implementation
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Error Handling
|
||
|
- Use `anyhow::Result<T>` for return types
|
||
|
- Provide descriptive error messages with context
|
||
|
- Handle specific error cases appropriately
|
||
|
- Log errors with relevant context
|
||
|
- Example:
|
||
|
```rust
|
||
|
match operation() {
|
||
|
Ok(result) => Ok(result),
|
||
|
Err(diesel::NotFound) => Err(anyhow!("Resource not found")),
|
||
|
Err(e) => {
|
||
|
tracing::error!("Operation failed: {}", e);
|
||
|
Err(anyhow!("Operation failed: {}", e))
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Database Operations
|
||
|
- Use the connection pool: `get_pg_pool().get().await?`
|
||
|
- Run concurrent operations when possible
|
||
|
- Use transactions for related operations
|
||
|
- Handle database-specific errors appropriately
|
||
|
- Example:
|
||
|
```rust
|
||
|
let pool = get_pg_pool();
|
||
|
let mut conn = pool.get().await?;
|
||
|
|
||
|
diesel::update(table)
|
||
|
.filter(conditions)
|
||
|
.set(values)
|
||
|
.execute(&mut conn)
|
||
|
.await?
|
||
|
```
|
||
|
|
||
|
### Concurrency
|
||
|
- Use `tokio::spawn` for concurrent operations
|
||
|
- Use `futures::try_join_all` for parallel processing
|
||
|
- Be mindful of connection pool limits
|
||
|
- Example:
|
||
|
```rust
|
||
|
let thread_future = tokio::spawn(async move {
|
||
|
// Database operation 1
|
||
|
});
|
||
|
|
||
|
let messages_future = tokio::spawn(async move {
|
||
|
// Database operation 2
|
||
|
});
|
||
|
|
||
|
let (thread_result, messages_result) = tokio::join!(thread_future, messages_future);
|
||
|
```
|
||
|
|
||
|
### Logging
|
||
|
- Use structured logging with `tracing`
|
||
|
- Include relevant context in log messages
|
||
|
- Log at appropriate levels (info, warn, error)
|
||
|
- Example:
|
||
|
```rust
|
||
|
tracing::info!(
|
||
|
resource_id = %id,
|
||
|
user_id = %user.id,
|
||
|
"Processing resource action"
|
||
|
);
|
||
|
```
|
||
|
|
||
|
### Type Definitions
|
||
|
- Use `serde` for serialization/deserialization
|
||
|
- Define clear, reusable types
|
||
|
- Use appropriate validation
|
||
|
- Example:
|
||
|
```rust
|
||
|
#[derive(Debug, Serialize, Deserialize)]
|
||
|
pub struct ResourceRequest {
|
||
|
pub id: Uuid,
|
||
|
pub name: String,
|
||
|
#[serde(default)]
|
||
|
pub options: Vec<String>,
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Integration with REST and WebSocket APIs
|
||
|
- Handlers should be independent of transport mechanism
|
||
|
- Same handler can be used by both REST and WebSocket endpoints
|
||
|
- Handlers should focus on business logic, not HTTP/WebSocket specifics
|
||
|
- Example:
|
||
|
```rust
|
||
|
// In REST route
|
||
|
pub async fn rest_endpoint(
|
||
|
Json(payload): Json<HandlerRequest>,
|
||
|
user: User,
|
||
|
) -> Result<Json<HandlerResponse>, AppError> {
|
||
|
let result = handler::action_resource_handler(payload, user).await?;
|
||
|
Ok(Json(result))
|
||
|
}
|
||
|
|
||
|
// In WebSocket handler
|
||
|
async fn ws_message_handler(message: WsMessage, user: User) -> Result<WsResponse> {
|
||
|
let payload: HandlerRequest = serde_json::from_str(&message.payload)?;
|
||
|
let result = handler::action_resource_handler(payload, user).await?;
|
||
|
Ok(WsResponse::new(result))
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## CLI Integration
|
||
|
- Handler types should be reusable in CLI commands
|
||
|
- CLI commands should use the same handlers as the API when possible
|
||
|
- Example:
|
||
|
```rust
|
||
|
// In CLI command
|
||
|
pub fn cli_command(args: &ArgMatches) -> Result<()> {
|
||
|
let request = HandlerRequest {
|
||
|
// Parse from args
|
||
|
};
|
||
|
|
||
|
let result = tokio::runtime::Runtime::new()?.block_on(async {
|
||
|
handler::action_resource_handler(request, mock_user()).await
|
||
|
})?;
|
||
|
|
||
|
println!("{}", serde_json::to_string_pretty(&result)?);
|
||
|
Ok(())
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Testing
|
||
|
- Write unit tests for handlers
|
||
|
- Mock database and external dependencies
|
||
|
- Test error cases and edge conditions
|
||
|
- Example:
|
||
|
```rust
|
||
|
#[tokio::test]
|
||
|
async fn test_action_resource_handler() {
|
||
|
// Setup test data
|
||
|
let request = HandlerRequest { /* ... */ };
|
||
|
let user = mock_user();
|
||
|
|
||
|
// Call handler
|
||
|
let result = action_resource_handler(request, user).await;
|
||
|
|
||
|
// Assert expectations
|
||
|
assert!(result.is_ok());
|
||
|
let response = result.unwrap();
|
||
|
assert_eq!(response.field, expected_value);
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Common Patterns
|
||
|
- Retrieve data from database
|
||
|
- Process and transform data
|
||
|
- Interact with external services
|
||
|
- Return structured response
|
||
|
- Handle errors and edge cases
|
||
|
- Log relevant information
|
||
|
|
||
|
## Glob Pattern
|
||
|
The glob pattern for this rule is: `libs/handlers/**/*.rs`
|