Adds RequestHooks trait to enable intercepting and modifying S3 API requests at key points in the request lifecycle. This enables implementing cross-cutting concerns like load balancing, telemetry, and debug logging without modifying the core request handling logic. --------- Co-authored-by: Tobias Pütz <tobias@minio.io>
10 KiB
Claude Code Style Guide for MinIO Rust SDK
- Only provide actionable feedback.
- Exclude code style comments on generated files. These will have a header signifying that.
- Use github markdown folded sections for all items.
- Do not use emojis.
- Do not add a "feel good" section.
Copyright Header
All source files that haven't been generated MUST include the following copyright header:
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
// Copyright 20?? MinIO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Code Style Guidelines
Ignore files
- Ignore files from processing mentioned under '.gitignore'
Comments
- NO redundant comments - Code should be self-documenting
- Avoid obvious comments like
// Set x to 5forlet x = 5; - Only add comments when they explain WHY, not WHAT
- Document complex algorithms or non-obvious business logic
Critical Code Patterns
Builder Pattern
All S3 API requests MUST use the builder pattern, with documentation similar to the following example (adapted for each specific API)
/// Argument builder for the [`AppendObject`](https://docs.aws.amazon.com/AmazonS3/latest/userguide/directory-buckets-objects-append.html) S3 API operation.
///
/// This struct constructs the parameters required for the [`Client::append_object`](crate::s3::client::Client::append_object) method.
Key Requirements:
- The aws docs url must exist.
Error Handling Pattern
All Rust SDK methods should follow consistent error handling patterns:
impl Client {
pub async fn operation_name(&self, args: &OperationArgs) -> Result<OperationResponse, Error> {
// Validate inputs early
args.validate()?;
// Build request
let request = self.build_request(args)?;
// Execute with proper error propagation
let response = self.execute(request).await?;
// Parse and return
OperationResponse::from_response(response)
}
}
Rust-Specific Best Practices
-
Ownership and Borrowing
- Prefer
&strover&Stringin function parameters - Use
AsRef<str>orInto<String>for flexible string parameters - Return owned types from functions unless lifetime annotations are clear
- Prefer
-
Type Safety
- Use
#[must_use]attribute for functions returning important values - Prefer strong typing over primitive obsession
- Use newtypes for domain-specific values
- Use
-
Unsafe Code
- Avoid
unsafecode unless absolutely necessary - Document all safety invariants when
unsafeis required - Isolate
unsafeblocks and keep them minimal
- Avoid
-
Performance
- Use
Cow<'_, str>to avoid unnecessary allocations - Prefer iterators over collecting into intermediate vectors
- Use
Box<dyn Trait>sparingly; prefer generics when possible
- Use
-
Async Patterns
- Use
tokio::select!for concurrent operations - Avoid blocking operations in async contexts
- Use
async-traitfor async trait methods
- Use
Code Quality Principles
Why Code Quality Standards Are Mandatory
Code quality standards are critical business requirements for MinIO Rust SDK:
- Enterprise Data Safety: A single bug can corrupt terabytes of customer data across distributed systems
- Code Efficiency: MinIO Rust SDK code must be efficient and performant
- Scalability: MinIO Rust SDK must be able to handle thousands of concurrent requests
- High Availability: Systems must handle failures gracefully - unpredictable code creates cascading failures
- Developer Velocity: New team members must understand complex distributed systems quickly and safely
Predictable Code Requirements
Code must exhibit deterministic behavior to ensure system reliability:
- Managed State: Use Arc<Mutex<>> or Arc<RwLock<>> for shared state that needs thread-safe access across async operations
- Explicit Dependencies: Business logic dependencies should be passed as parameters or dependency injection
- Deterministic Operations: Avoid time-dependent logic, random values, or platform-specific behavior in core paths
- Consistent Error Handling: Same error conditions must always produce identical error responses
- Idempotent Operations: Operations should be safely repeatable without unintended side effects
Readability Standards
Complex distributed systems code must remain human-readable:
- Self-Documenting Code: Function and variable names should clearly express business intent
- Consistent Patterns: Follow established patterns (HTTP handlers, error handling, logging)
- Logical Flow: Code should read as a clear narrative from top to bottom
- Minimal Cognitive Load: Each function should have a single, well-defined responsibility
- Clear Abstractions: Break complex operations into well-named, focused helper functions
Separation of Concerns
Architectural layers must maintain clear boundaries:
- Handler Layer: HTTP request/response processing, input validation, context creation
- Service Layer: Business logic orchestration, data transformation
- Storage Layer: Data persistence, replication, consistency management
- Utility Layer: Reusable helpers with no business logic dependencies
- Shared State Coordination: Use thread-safe primitives (Arc, Mutex, RwLock) for components needing consistent views
Functions and Methods
- Keep functions focused on a single responsibility
- Use descriptive names that clearly indicate purpose and business intent
- Prefer early returns to reduce nesting complexity
- Error handling should be immediate and explicit
- Function length guideline: Most functions should be under 100 lines; handlers may be longer due to validation logic
- Parameter limits: Prefer structs over long parameter lists for better maintainability
Variables
- Use meaningful variable names that reflect business concepts
- Variable names should reflect usage frequency: frequent variables can be shorter
- Constants should use SCREAMING_SNAKE_CASE (e.g.,
MAX_RETRIES,DEFAULT_TIMEOUT) - Static variables should be clearly identified with proper safety documentation
- Prefer
constoverstaticwhen possible for compile-time constants
Developer Documentation
All significant features must include developer documentation in the docs/ directory:
- API Documentation: New endpoints must have usage examples in
docs/ - Architecture Decisions: Complex algorithms or design patterns should be documented
- Configuration Changes: New config options must be documented with examples
- Integration Guides: External system integrations need clear setup instructions
- Future Developer Context: Document WHY decisions were made, not just WHAT was implemented
Testing Requirements
Why Unit Tests Are Mandatory
Unit tests are non-negotiable in this project for critical business reasons:
- Data Integrity: MinIO Rust SDK handles enterprise-critical data. A single bug can cause data loss affecting thousands of users
- Security Compliance: Financial and healthcare customers require verifiable code quality. Tests provide audit trails
- Multi-tenant Reliability: One customer's workload cannot impact another's. Tests ensure proper isolation
- Performance SLAs: Enterprise customers have strict performance requirements. Tests validate behavior under load
- API Stability: Breaking changes can affect thousands of applications. Tests prevent regressions
- Distributed System Complexity: Complex interactions between storage nodes require comprehensive testing
Mandatory Unit Tests
EVERY implementation MUST include unit tests without being explicitly asked. Follow these patterns:
- Test functions must use
#[test]or#[tokio::test]attributes - Use parameterized tests or loop through test cases for multiple scenarios
- Cover both success and error cases, including edge conditions
- Mock external dependencies appropriately
- Test coverage guideline: Aim for comprehensive coverage of new code paths
- Include negative tests for error conditions and boundary cases
- Add benchmarks for performance-critical code paths
Improvement Suggestions
Claude will periodically analyze the codebase and suggest:
- Missing test coverage areas
- Performance optimizations
- Code refactoring opportunities
- Security improvements
- Documentation gaps
Testing Commands
Pre-commit Checklist
Before any code changes:
- ✅ Run
cargo fmt --allto check and fix code formatting - ✅ Run
cargo testto ensure all tests pass - ✅ Run
cargo clippy --all-targets --all-features --workspace -- -D warningsto check for common mistakes and ensure no warnings - ✅ Ensure new code has appropriate test coverage
- ✅ Verify no redundant comments are added
Directory Structure Conventions
/src- Main library source code/tests- Integration tests/examples- Example usage code/docs- Documentation/benches- Performance benchmarks
Common Patterns to Follow
Logging
Use the log crate with appropriate macros:
use log::{debug, error, info, trace, warn};
// Examples:
info!("Starting operation: {}", operation_name);
debug!("Request details: {:?}", request);
error!("Operation failed: {}", err);
Error Handling
Use the Result type with proper error propagation:
use crate::s3::error::Error;
fn operation() -> Result<Response, Error> {
let result = risky_operation()?;
Ok(process(result))
}
Quick Reference
- Fix formatting:
cargo fmt --all - Run tests:
cargo test - Run specific test:
cargo test test_name - Check code:
cargo clippy --all-targets --all-features --workspace -- -D warnings - Build project:
cargo build --release - Generate docs:
cargo doc --open