From 3e915cf9041bb8341003b304de3ce7e387e9a18d Mon Sep 17 00:00:00 2001 From: Tyr Chen Date: Sat, 31 May 2025 15:54:39 -0700 Subject: [PATCH] feature: add more rules for config and observability. Bump version to 0.1.2. --- .cursor/rules/rust/complex/workspace.mdc | 224 ++--- .cursor/rules/rust/core/dependencies.mdc | 23 + .cursor/rules/rust/features/configuration.mdc | 557 +++++++++++++ .cursor/rules/rust/features/observability.mdc | 772 ++++++++++++++++++ .cursor/rules/rust/main.mdc | 42 +- LICENSE.md | 7 + README.md | 157 +++- specs/instructions.md | 4 + specs/rust-rules-improvement-plan.md | 221 +++++ 9 files changed, 1891 insertions(+), 116 deletions(-) create mode 100644 .cursor/rules/rust/features/configuration.mdc create mode 100644 .cursor/rules/rust/features/observability.mdc create mode 100644 LICENSE.md create mode 100644 specs/rust-rules-improvement-plan.md diff --git a/.cursor/rules/rust/complex/workspace.mdc b/.cursor/rules/rust/complex/workspace.mdc index 7fc4a55..d30af31 100644 --- a/.cursor/rules/rust/complex/workspace.mdc +++ b/.cursor/rules/rust/complex/workspace.mdc @@ -40,66 +40,111 @@ graph TD style LayeredArch fill:#d94dbb,stroke:#a3378a,color:white ``` -## ๐Ÿ—๏ธ SUBSYSTEM-BASED WORKSPACE ARCHITECTURE +## ๐Ÿ—๏ธ SERVICE-ORIENTED WORKSPACE ARCHITECTURE ```mermaid graph TD - Workspace["E-Commerce Platform Workspace"] --> Shared["Shared Infrastructure"] - Workspace --> UserMgmt["User Management
Subsystem"] - Workspace --> ProductCatalog["Product Catalog
Subsystem"] - Workspace --> OrderMgmt["Order Management
Subsystem"] - Workspace --> Payment["Payment
Subsystem"] - Workspace --> Notification["Notification
Subsystem"] - Workspace --> Analytics["Analytics
Subsystem"] - Workspace --> Gateway["API Gateway
& Services"] + Workspace["Service Platform Workspace"] --> Shared["Shared Infrastructure"] + Workspace --> CoreServices["Core Services"] + Workspace --> BusinessServices["Business Services"] + Workspace --> PlatformServices["Platform Services"] Shared --> SharedTypes["shared-types
(domain primitives)"] - Shared --> SharedDb["shared-db
(database infrastructure)"] Shared --> SharedConfig["shared-config
(configuration)"] - Shared --> SharedEvents["shared-events
(event definitions)"] + Shared --> SharedObservability["shared-observability
(metrics & tracing)"] - UserMgmt --> UserCore["user-core
(business logic)"] - UserMgmt --> UserApi["user-api
(HTTP handlers)"] - UserMgmt --> UserWorker["user-worker
(background jobs)"] + CoreServices --> ServiceCore["service-core
(business logic)"] + CoreServices --> ServiceApi["service-api
(HTTP handlers)"] + CoreServices --> ServiceWorker["service-worker
(background jobs)"] - ProductCatalog --> ProductCore["product-core
(business logic)"] - ProductCatalog --> ProductApi["product-api
(HTTP handlers)"] - ProductCatalog --> ProductSearch["product-search
(search service)"] + BusinessServices --> BusinessCore["business-core
(domain logic)"] + BusinessServices --> BusinessApi["business-api
(API endpoints)"] + BusinessServices --> BusinessProcessor["business-processor
(workflow engine)"] - OrderMgmt --> OrderCore["order-core
(business logic)"] - OrderMgmt --> OrderApi["order-api
(HTTP handlers)"] - OrderMgmt --> OrderProcessor["order-processor
(workflow engine)"] - - Payment --> PaymentCore["payment-core
(business logic)"] - Payment --> PaymentApi["payment-api
(HTTP handlers)"] - Payment --> PaymentGateway["payment-gateway
(external integrations)"] - - Notification --> NotificationCore["notification-core
(business logic)"] - Notification --> NotificationService["notification-service
(delivery engine)"] - - Analytics --> AnalyticsCore["analytics-core
(business logic)"] - Analytics --> AnalyticsCollector["analytics-collector
(data collection)"] - Analytics --> AnalyticsReporter["analytics-reporter
(reporting service)"] - - Gateway --> ApiGateway["api-gateway
(unified API)"] - Gateway --> AdminCli["admin-cli
(administration tool)"] - Gateway --> MigrationTool["migration-tool
(database migrations)"] + PlatformServices --> Gateway["gateway
(API gateway)"] + PlatformServices --> AdminPanel["admin-panel
(administration)"] + PlatformServices --> HealthMonitor["health-monitor
(monitoring)"] style Workspace fill:#4da6ff,stroke:#0066cc,color:white style Shared fill:#4dbb5f,stroke:#36873f,color:white - style UserMgmt fill:#ffa64d,stroke:#cc7a30,color:white - style ProductCatalog fill:#d94dbb,stroke:#a3378a,color:white - style OrderMgmt fill:#4dbbbb,stroke:#368787,color:white - style Payment fill:#ff6b6b,stroke:#cc5555,color:white - style Notification fill:#9b59b6,stroke:#7d4796,color:white - style Analytics fill:#f39c12,stroke:#d68910,color:white - style Gateway fill:#2ecc71,stroke:#27ae60,color:white + style CoreServices fill:#ffa64d,stroke:#cc7a30,color:white + style BusinessServices fill:#d94dbb,stroke:#a3378a,color:white + style PlatformServices fill:#9b59b6,stroke:#7d4796,color:white ``` -## ๐Ÿ“ SUBSYSTEM-BASED DIRECTORY STRUCTURE +## ๐Ÿ”„ SERVICE CONFIGURATION MANAGEMENT + +### Hot-Reloadable Service Architecture +```rust +use arc_swap::ArcSwap; +use std::sync::Arc; +use tokio::sync::broadcast; + +// โœ… Service-oriented architecture with shared state +pub struct ServiceManager { + config: Arc>, + components: Vec>, + config_tx: broadcast::Sender>, +} + +impl ServiceManager +where + T: Clone + Send + Sync + 'static, +{ + pub fn new(initial_config: T) -> Self { + let (config_tx, _) = broadcast::channel(16); + + Self { + config: Arc::new(ArcSwap::from_pointee(initial_config)), + components: Vec::new(), + config_tx, + } + } + + pub fn add_component(&mut self, component: Arc) { + self.components.push(component); + } + + pub fn update_config(&self, new_config: T) { + let new_config = Arc::new(new_config); + self.config.store(new_config.clone()); + + // Notify all components of config change + for component in &self.components { + component.on_config_update(); + } + + // Broadcast to subscribers + let _ = self.config_tx.send(new_config); + } + + pub fn get_config(&self) -> Arc { + self.config.load_full() + } + + pub fn subscribe_to_config_changes(&self) -> broadcast::Receiver> { + self.config_tx.subscribe() + } +} + +pub trait ServiceComponent: Send + Sync { + fn on_config_update(&self); + fn service_name(&self) -> &str; + fn health_check(&self) -> ServiceHealth; +} + +#[derive(Debug, Clone)] +pub enum ServiceHealth { + Healthy, + Degraded { reason: String }, + Unhealthy { reason: String }, +} +``` + +## ๐Ÿ“ SERVICE-ORIENTED DIRECTORY STRUCTURE ``` -ecommerce-platform/ +service-platform/ โ”œโ”€โ”€ Cargo.toml # Workspace configuration โ”œโ”€โ”€ Cargo.lock # Locked dependencies โ”œโ”€โ”€ README.md # Platform overview @@ -111,98 +156,81 @@ ecommerce-platform/ โ”‚ โ”‚ โ”œโ”€โ”€ Cargo.toml โ”‚ โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”‚ โ”œโ”€โ”€ lib.rs -โ”‚ โ”‚ โ”œโ”€โ”€ ids.rs # UserId, ProductId, OrderId etc. -โ”‚ โ”‚ โ”œโ”€โ”€ money.rs # Money, Currency types -โ”‚ โ”‚ โ”œโ”€โ”€ address.rs # Address, Location types -โ”‚ โ”‚ โ”œโ”€โ”€ time.rs # Timestamp, DateRange types -โ”‚ โ”‚ โ””โ”€โ”€ pagination.rs # Pagination, Sorting types -โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€ shared-db/ # Database infrastructure -โ”‚ โ”‚ โ”œโ”€โ”€ Cargo.toml -โ”‚ โ”‚ โ””โ”€โ”€ src/ -โ”‚ โ”‚ โ”œโ”€โ”€ lib.rs -โ”‚ โ”‚ โ”œโ”€โ”€ connection.rs # Connection pooling -โ”‚ โ”‚ โ”œโ”€โ”€ transactions.rs # Transaction management -โ”‚ โ”‚ โ”œโ”€โ”€ migrations.rs # Migration framework -โ”‚ โ”‚ โ””โ”€โ”€ repositories/ # Base repository traits +โ”‚ โ”‚ โ”œโ”€โ”€ ids.rs # EntityId, RequestId types +โ”‚ โ”‚ โ”œโ”€โ”€ time.rs # Timestamp, Duration types +โ”‚ โ”‚ โ”œโ”€โ”€ pagination.rs # Pagination, Sorting types +โ”‚ โ”‚ โ””โ”€โ”€ validation.rs # Common validation rules โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ shared-config/ # Configuration management โ”‚ โ”‚ โ”œโ”€โ”€ Cargo.toml โ”‚ โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”‚ โ”œโ”€โ”€ lib.rs +โ”‚ โ”‚ โ”œโ”€โ”€ service.rs # Service configuration โ”‚ โ”‚ โ”œโ”€โ”€ database.rs # Database configuration -โ”‚ โ”‚ โ”œโ”€โ”€ redis.rs # Redis configuration -โ”‚ โ”‚ โ”œโ”€โ”€ auth.rs # Auth configuration -โ”‚ โ”‚ โ””โ”€โ”€ services.rs # Service discovery +โ”‚ โ”‚ โ”œโ”€โ”€ observability.rs # Metrics & tracing config +โ”‚ โ”‚ โ””โ”€โ”€ environment.rs # Environment-specific settings โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€ shared-events/ # Event definitions & messaging +โ”‚ โ””โ”€โ”€ shared-observability/ # Metrics & tracing infrastructure โ”‚ โ”œโ”€โ”€ Cargo.toml โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ lib.rs -โ”‚ โ”œโ”€โ”€ user_events.rs # UserRegistered, UserUpdated -โ”‚ โ”œโ”€โ”€ order_events.rs # OrderPlaced, OrderFulfilled -โ”‚ โ”œโ”€โ”€ payment_events.rs # PaymentProcessed, PaymentFailed -โ”‚ โ””โ”€โ”€ event_bus.rs # Event publishing infrastructure +โ”‚ โ”œโ”€โ”€ metrics.rs # Metrics collection patterns +โ”‚ โ”œโ”€โ”€ tracing.rs # Distributed tracing setup +โ”‚ โ”œโ”€โ”€ health.rs # Health check framework +โ”‚ โ””โ”€โ”€ middleware.rs # Observability middleware โ”‚ -โ”œโ”€โ”€ subsystems/ # Business subsystems -โ”‚ โ”œโ”€โ”€ user-management/ # User & Authentication subsystem -โ”‚ โ”‚ โ”œโ”€โ”€ user-core/ # Business logic +โ”œโ”€โ”€ services/ # Business services +โ”‚ โ”œโ”€โ”€ core-services/ # Core business logic services +โ”‚ โ”‚ โ”œโ”€โ”€ service-core/ # Core business logic โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Cargo.toml โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ lib.rs -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ entities/ # User, Profile, Session -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ services/ # AuthService, ProfileService -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ repositories/ # UserRepository trait +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ entities/ # Core domain entities +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ services/ # Business service implementations +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ repositories/ # Repository trait definitions โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ errors.rs โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”œโ”€โ”€ user-api/ # HTTP API handlers +โ”‚ โ”‚ โ”œโ”€โ”€ service-api/ # HTTP API handlers โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Cargo.toml โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ lib.rs -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ handlers/ # Auth, profile endpoints -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ middleware/ # Auth middleware +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ handlers/ # HTTP endpoint handlers +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ middleware/ # API middleware โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ dto/ # Request/response DTOs โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ validation.rs โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€ user-worker/ # Background jobs +โ”‚ โ”‚ โ””โ”€โ”€ service-worker/ # Background processing โ”‚ โ”‚ โ”œโ”€โ”€ Cargo.toml โ”‚ โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”‚ โ”œโ”€โ”€ main.rs -โ”‚ โ”‚ โ”œโ”€โ”€ jobs/ # Email verification, cleanup -โ”‚ โ”‚ โ””โ”€โ”€ handlers.rs +โ”‚ โ”‚ โ”œโ”€โ”€ jobs/ # Background job definitions +โ”‚ โ”‚ โ””โ”€โ”€ processors.rs โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€ product-catalog/ # Product management subsystem -โ”‚ โ”‚ โ”œโ”€โ”€ product-core/ # Business logic +โ”‚ โ”œโ”€โ”€ business-services/ # Business-specific services +โ”‚ โ”‚ โ”œโ”€โ”€ business-core/ # Business domain logic โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Cargo.toml โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ lib.rs -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ entities/ # Product, Category, Inventory -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ services/ # ProductService, InventoryService -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ repositories/ +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ entities/ # Business domain entities +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ services/ # Business logic services +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ workflows/ # Business process workflows โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ errors.rs โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”œโ”€โ”€ product-api/ # HTTP API handlers +โ”‚ โ”‚ โ”œโ”€โ”€ business-api/ # Business API endpoints โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Cargo.toml โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ lib.rs -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ handlers/ # Product CRUD, search -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ dto/ -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ filters.rs +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ handlers/ # Business endpoint handlers +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ dto/ # Business DTOs +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ validators.rs โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ””โ”€โ”€ product-search/ # Search engine service +โ”‚ โ”‚ โ””โ”€โ”€ business-processor/ # Business workflow engine โ”‚ โ”‚ โ”œโ”€โ”€ Cargo.toml โ”‚ โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”‚ โ”œโ”€โ”€ lib.rs -โ”‚ โ”‚ โ”œโ”€โ”€ indexing/ # ElasticSearch integration -โ”‚ โ”‚ โ”œโ”€โ”€ queries/ # Search query builders -โ”‚ โ”‚ โ””โ”€โ”€ ranking.rs # Relevance scoring -โ”‚ โ”‚ -โ”‚ โ”œโ”€โ”€ order-management/ # Order processing subsystem -โ”‚ โ”‚ โ”œโ”€โ”€ order-core/ # Business logic -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ Cargo.toml -โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ src/ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ lib.rs -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ entities/ # Order, OrderItem, Cart +โ”‚ โ”‚ โ”œโ”€โ”€ workflows/ # Workflow definitions +โ”‚ โ”‚ โ”œโ”€โ”€ steps/ # Workflow step implementations +โ”‚ โ”‚ โ””โ”€โ”€ executor.rs # Workflow execution engine โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ services/ # OrderService, CartService โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ state_machine/ # Order state transitions โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ repositories/ diff --git a/.cursor/rules/rust/core/dependencies.mdc b/.cursor/rules/rust/core/dependencies.mdc index f723648..44c78e3 100644 --- a/.cursor/rules/rust/core/dependencies.mdc +++ b/.cursor/rules/rust/core/dependencies.mdc @@ -143,6 +143,28 @@ colored = "2.0" # Terminal colors console = "0.15" # Terminal utilities ``` +### Configuration Management +```toml +figment = { version = "0.10", features = ["yaml", "toml", "env"] } +notify = "6.0" # File watching +arc-swap = "1.0" # Atomic configuration updates +validator = { version = "0.18", features = ["derive"] } +``` + +### Observability +```toml +prometheus = "0.13" # Metrics collection +opentelemetry = "0.23" # Distributed tracing +tracing-opentelemetry = "0.23" # Tracing integration +dashmap = "6.0" # Lock-free concurrent maps +``` + +### Frontend Integration +```toml +rust-embed = "8.0" # Static asset embedding +mime_guess = "2.0" # MIME type detection +``` + ### gRPC/Protobuf ```toml tonic = { version = "0.13", features = ["transport", "codegen", "prost"] } @@ -163,6 +185,7 @@ assert_cmd = "2.0" # CLI testing predicates = "3.0" # Test assertions axum-test = "15.0" # Axum testing tokio-test = "0.4" # Tokio testing utilities +temp-env = "0.3" # Environment variable testing ``` ## ๐Ÿ”ง FEATURE FLAG STRATEGY diff --git a/.cursor/rules/rust/features/configuration.mdc b/.cursor/rules/rust/features/configuration.mdc new file mode 100644 index 0000000..1154a58 --- /dev/null +++ b/.cursor/rules/rust/features/configuration.mdc @@ -0,0 +1,557 @@ +--- +description: +globs: +alwaysApply: false +--- +# โš™๏ธ RUST CONFIGURATION MANAGEMENT + +> **TL;DR:** Comprehensive patterns for configuration management in Rust applications, including multi-format parsing, validation, hot-reloading, and environment-based overrides. + +## ๐Ÿ” CONFIGURATION STRATEGY + +```mermaid +graph TD + Start["Configuration Needs"] --> Format{"Configuration
Format?"} + + Format -->|Single| SingleFormat["Single Format Parsing"] + Format -->|Multiple| MultiFormat["Multi-Format Support"] + + SingleFormat --> Validation["Configuration Validation"] + MultiFormat --> Validation + + Validation --> Environment["Environment Overrides"] + Environment --> Runtime{"Runtime
Updates?"} + + Runtime -->|Static| StaticConfig["Static Configuration"] + Runtime -->|Dynamic| HotReload["Hot Reloading"] + + StaticConfig --> Testing["Configuration Testing"] + HotReload --> Watching["File System Watching"] + Watching --> AtomicUpdate["Atomic Updates"] + AtomicUpdate --> Testing + + Testing --> Production["Production Configuration"] + + style Start fill:#4da6ff,stroke:#0066cc,color:white + style MultiFormat fill:#4dbb5f,stroke:#36873f,color:white + style HotReload fill:#ffa64d,stroke:#cc7a30,color:white + style AtomicUpdate fill:#d94dbb,stroke:#a3378a,color:white +``` + +## ๐ŸŽฏ CONFIGURATION PRINCIPLES + +### Multi-Format Configuration Support +```rust +use figment::{Figment, providers::{Format, Yaml, Toml, Json, Env}}; +use serde::{Deserialize, Serialize}; +use validator::{Validate, ValidationError}; + +// โœ… Configuration structure with validation +#[derive(Debug, Clone, Serialize, Deserialize, Validate)] +#[serde(rename_all = "snake_case")] +pub struct AppConfig { + #[validate(length(min = 1, max = 100))] + pub name: String, + + #[validate(range(min = 1, max = 65535))] + pub port: u16, + + #[serde(default = "default_host")] + #[validate(length(min = 1))] + pub host: String, + + #[serde(default)] + pub features: Vec, + + #[validate(nested)] + pub database: DatabaseConfig, + + #[serde(default)] + #[validate(nested)] + pub logging: LoggingConfig, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Validate)] +pub struct DatabaseConfig { + #[validate(url)] + pub url: String, + + #[validate(range(min = 1, max = 1000))] + #[serde(default = "default_pool_size")] + pub pool_size: u32, + + #[serde(default = "default_timeout")] + pub timeout_seconds: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Validate)] +pub struct LoggingConfig { + #[serde(default = "default_log_level")] + pub level: String, + + #[serde(default)] + pub json_format: bool, +} + +// Default value functions +fn default_host() -> String { "0.0.0.0".to_string() } +fn default_pool_size() -> u32 { 10 } +fn default_timeout() -> u64 { 30 } +fn default_log_level() -> String { "info".to_string() } + +impl AppConfig { + /// Load configuration from multiple sources with precedence: + /// 1. Environment variables (highest priority) + /// 2. config.yaml file + /// 3. config.toml file + /// 4. Default values (lowest priority) + pub fn load() -> Result { + let config = Figment::new() + .merge(Toml::file("config.toml")) + .merge(Yaml::file("config.yaml")) + .merge(Json::file("config.json")) + .merge(Env::prefixed("APP_")) + .extract()?; + + // Validate the configuration + config.validate() + .map_err(ConfigError::Validation)?; + + Ok(config) + } + + /// Load from a specific file path + pub fn from_file>(path: P) -> Result { + let path = path.as_ref(); + let extension = path.extension() + .and_then(|ext| ext.to_str()) + .ok_or_else(|| ConfigError::UnsupportedFormat("Unknown file extension".to_string()))?; + + let figment = match extension.to_lowercase().as_str() { + "yaml" | "yml" => Figment::new().merge(Yaml::file(path)), + "toml" => Figment::new().merge(Toml::file(path)), + "json" => Figment::new().merge(Json::file(path)), + ext => return Err(ConfigError::UnsupportedFormat(ext.to_string())), + }; + + let config = figment + .merge(Env::prefixed("APP_")) + .extract()?; + + config.validate() + .map_err(ConfigError::Validation)?; + + Ok(config) + } +} +``` + +## ๐Ÿ”„ HOT CONFIGURATION RELOADING + +### Atomic Configuration Updates +```rust +use arc_swap::ArcSwap; +use notify::{RecommendedWatcher, RecursiveMode, Result as NotifyResult, Watcher}; +use std::sync::Arc; +use tokio::sync::{broadcast, mpsc}; + +// โœ… Configuration manager with hot reloading +pub struct ConfigManager { + current: Arc>, + reload_tx: broadcast::Sender>, + config_path: std::path::PathBuf, +} + +impl ConfigManager +where + T: for<'de> Deserialize<'de> + Validate + Clone + Send + Sync + 'static, +{ + pub fn new(initial_config: T, config_path: impl Into) -> Self { + let (reload_tx, _) = broadcast::channel(16); + let current = Arc::new(ArcSwap::from_pointee(initial_config)); + + Self { + current, + reload_tx, + config_path: config_path.into(), + } + } + + /// Get current configuration + pub fn get(&self) -> Arc { + self.current.load_full() + } + + /// Subscribe to configuration updates + pub fn subscribe(&self) -> broadcast::Receiver> { + self.reload_tx.subscribe() + } + + /// Start watching for configuration file changes + pub async fn start_watching(&self) -> Result<(), ConfigError> { + let (tx, mut rx) = mpsc::channel(1); + let config_path = self.config_path.clone(); + + // File system watcher + let mut watcher = RecommendedWatcher::new( + move |res: NotifyResult| { + if let Ok(event) = res { + if event.kind.is_modify() { + let _ = tx.try_send(()); + } + } + }, + notify::Config::default(), + )?; + + watcher.watch(&config_path, RecursiveMode::NonRecursive)?; + + let current = self.current.clone(); + let reload_tx = self.reload_tx.clone(); + let config_path = self.config_path.clone(); + + // Spawn reload handler + tokio::spawn(async move { + while rx.recv().await.is_some() { + // Small delay to ensure file write is complete + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + + match Self::load_config_from_file(&config_path) { + Ok(new_config) => { + let new_config = Arc::new(new_config); + current.store(new_config.clone()); + + // Notify subscribers + if let Err(e) = reload_tx.send(new_config) { + tracing::warn!("Failed to notify config subscribers: {}", e); + } else { + tracing::info!("Configuration reloaded successfully"); + } + } + Err(e) => { + tracing::error!("Failed to reload configuration: {}", e); + } + } + } + }); + + // Keep watcher alive + std::mem::forget(watcher); + Ok(()) + } + + /// Manually reload configuration + pub async fn reload(&self) -> Result<(), ConfigError> { + let new_config = Self::load_config_from_file(&self.config_path)?; + let new_config = Arc::new(new_config); + + self.current.store(new_config.clone()); + self.reload_tx.send(new_config) + .map_err(|_| ConfigError::ReloadFailed)?; + + Ok(()) + } + + fn load_config_from_file(path: &std::path::Path) -> Result { + // Implementation depends on your config format + // This is a placeholder - use figment or similar + todo!("Implement config loading based on file type") + } +} + +/// Service component that can react to configuration changes +pub trait ConfigurableService: Send + Sync { + type Config; + + fn on_config_update(&self, config: &Self::Config); +} + +/// Helper for services that need configuration updates +pub struct ServiceManager { + config_manager: ConfigManager, + services: Vec>, +} + +impl ServiceManager +where + T: for<'de> Deserialize<'de> + Validate + Clone + Send + Sync + 'static, + S: ConfigurableService, +{ + pub fn new(config: T, config_path: impl Into) -> Self { + Self { + config_manager: ConfigManager::new(config, config_path), + services: Vec::new(), + } + } + + pub fn add_service(&mut self, service: Arc) { + self.services.push(service); + } + + pub async fn start(&self) -> Result<(), ConfigError> { + let mut config_updates = self.config_manager.subscribe(); + + // Start configuration watching + self.config_manager.start_watching().await?; + + // Spawn task to handle config updates + let services = self.services.clone(); + tokio::spawn(async move { + while let Ok(new_config) = config_updates.recv().await { + for service in &services { + service.on_config_update(&new_config); + } + } + }); + + Ok(()) + } + + pub fn get_config(&self) -> Arc { + self.config_manager.get() + } +} +``` + +## ๐ŸŒ ENVIRONMENT-BASED CONFIGURATION + +### Environment Override Patterns +```rust +use std::env; + +// โœ… Environment-aware configuration +impl AppConfig { + /// Create configuration for different environments + pub fn for_environment(env: Environment) -> Result { + let mut figment = Figment::new(); + + // Base configuration + figment = match env { + Environment::Development => figment + .merge(Toml::file("config/development.toml")) + .merge(Yaml::file("config/development.yaml")), + Environment::Testing => figment + .merge(Toml::file("config/testing.toml")) + .merge(Yaml::file("config/testing.yaml")), + Environment::Production => figment + .merge(Toml::file("config/production.toml")) + .merge(Yaml::file("config/production.yaml")), + }; + + // Always apply environment variables last + let config = figment + .merge(Env::prefixed("APP_")) + .extract()?; + + config.validate() + .map_err(ConfigError::Validation)?; + + Ok(config) + } + + /// Detect environment from environment variable + pub fn detect_environment() -> Environment { + env::var("APP_ENV") + .or_else(|_| env::var("ENVIRONMENT")) + .unwrap_or_else(|_| "development".to_string()) + .parse() + .unwrap_or(Environment::Development) + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Environment { + Development, + Testing, + Production, +} + +impl std::str::FromStr for Environment { + type Err = String; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "development" | "dev" => Ok(Environment::Development), + "testing" | "test" => Ok(Environment::Testing), + "production" | "prod" => Ok(Environment::Production), + _ => Err(format!("Unknown environment: {}", s)), + } + } +} +``` + +## ๐Ÿงช CONFIGURATION TESTING + +### Testing Configuration Patterns +```rust +#[cfg(test)] +mod tests { + use super::*; + use tempfile::NamedTempFile; + use std::io::Write; + + #[test] + fn test_config_validation() { + // โœ… Test configuration validation + let config = AppConfig { + name: "".to_string(), // Invalid: empty name + port: 70000, // Invalid: port too high + host: "localhost".to_string(), + features: vec!["feature1".to_string()], + database: DatabaseConfig { + url: "invalid-url".to_string(), // Invalid: not a valid URL + pool_size: 10, + timeout_seconds: 30, + }, + logging: LoggingConfig { + level: "info".to_string(), + json_format: false, + }, + }; + + let validation_result = config.validate(); + assert!(validation_result.is_err()); + } + + #[test] + fn test_yaml_config_loading() -> Result<(), Box> { + // โœ… Test loading from YAML + let yaml_content = r#" +name: "test-app" +port: 8080 +host: "localhost" +features: + - "feature1" + - "feature2" +database: + url: "postgresql://localhost/test" + pool_size: 5 + timeout_seconds: 60 +logging: + level: "debug" + json_format: true +"#; + + let mut temp_file = NamedTempFile::new()?; + write!(temp_file, "{}", yaml_content)?; + + let config = AppConfig::from_file(temp_file.path())?; + assert_eq!(config.name, "test-app"); + assert_eq!(config.port, 8080); + assert_eq!(config.features.len(), 2); + + Ok(()) + } + + #[test] + fn test_environment_override() { + // โœ… Test environment variable overrides + temp_env::with_vars([ + ("APP_NAME", Some("env-override-app")), + ("APP_PORT", Some("9090")), + ], || { + // Test that environment variables override file values + // Implementation would use figment to merge env vars + }); + } + + #[tokio::test] + async fn test_hot_reload() -> Result<(), Box> { + // โœ… Test hot reloading functionality + let initial_config = AppConfig { + name: "initial".to_string(), + port: 8080, + host: "localhost".to_string(), + features: vec![], + database: DatabaseConfig { + url: "postgresql://localhost/initial".to_string(), + pool_size: 10, + timeout_seconds: 30, + }, + logging: LoggingConfig { + level: "info".to_string(), + json_format: false, + }, + }; + + let temp_file = NamedTempFile::new()?; + let config_manager = ConfigManager::new(initial_config, temp_file.path()); + + // Test that we can get the initial config + let current = config_manager.get(); + assert_eq!(current.name, "initial"); + + // Test subscription to updates + let mut updates = config_manager.subscribe(); + + // Simulate config file change and reload + // ... implementation details + + Ok(()) + } +} +``` + +## ๐Ÿšจ CONFIGURATION ERROR HANDLING + +### Comprehensive Error Types +```rust +#[derive(Debug, thiserror::Error)] +pub enum ConfigError { + #[error("Configuration validation failed: {0}")] + Validation(#[from] validator::ValidationErrors), + + #[error("Failed to parse configuration: {0}")] + Parse(#[from] figment::Error), + + #[error("Unsupported configuration format: {0}")] + UnsupportedFormat(String), + + #[error("Failed to watch configuration file: {0}")] + FileWatch(#[from] notify::Error), + + #[error("Configuration reload failed")] + ReloadFailed, + + #[error("Environment variable error: {0}")] + Environment(#[from] std::env::VarError), + + #[error("I/O error: {0}")] + Io(#[from] std::io::Error), +} + +impl ConfigError { + pub fn is_validation_error(&self) -> bool { + matches!(self, ConfigError::Validation(_)) + } + + pub fn is_file_error(&self) -> bool { + matches!(self, ConfigError::FileWatch(_) | ConfigError::Io(_)) + } +} + +pub type Result = std::result::Result; +``` + +## โœ… CONFIGURATION MANAGEMENT CHECKLIST + +```markdown +### Configuration Implementation Verification +- [ ] Support multiple configuration formats (YAML, TOML, JSON) +- [ ] Implement comprehensive validation with validator +- [ ] Environment variable overrides work correctly +- [ ] Hot reloading implemented with atomic updates +- [ ] File system watching for configuration changes +- [ ] Environment-specific configuration files +- [ ] Proper error handling with contextual information +- [ ] Configuration testing with temporary files +- [ ] Default values provided for optional fields +- [ ] Configuration changes broadcast to subscribers +- [ ] Graceful degradation on configuration errors +- [ ] Documentation with examples for each format +- [ ] Secrets handling (environment variables only) +- [ ] Configuration validation on startup +- [ ] Performance considerations for frequent access +``` + +This configuration management guide provides robust patterns for handling complex configuration requirements in modern Rust applications. diff --git a/.cursor/rules/rust/features/observability.mdc b/.cursor/rules/rust/features/observability.mdc new file mode 100644 index 0000000..82b9d64 --- /dev/null +++ b/.cursor/rules/rust/features/observability.mdc @@ -0,0 +1,772 @@ +--- +description: +globs: +alwaysApply: false +--- +# ๐Ÿ“Š RUST OBSERVABILITY PATTERNS + +> **TL;DR:** Comprehensive observability patterns for Rust applications, including lock-free metrics collection, distributed tracing, health checks, and monitoring integration. + +## ๐Ÿ” OBSERVABILITY STRATEGY + +```mermaid +graph TD + Start["Observability Needs"] --> MetricsQ{"Metrics
Required?"} + Start --> TracingQ{"Distributed
Tracing?"} + Start --> HealthQ{"Health
Checks?"} + + MetricsQ -->|Yes| MetricsType{"Metrics
Type?"} + MetricsType -->|Counters| Counters["Lock-free Counters"] + MetricsType -->|Histograms| Histograms["Response Time Tracking"] + MetricsType -->|Gauges| Gauges["Current State Metrics"] + + TracingQ -->|Yes| TracingType{"Tracing
Scope?"} + TracingType -->|Application| AppTracing["Application Tracing"] + TracingType -->|Distributed| DistTracing["Distributed Tracing"] + + HealthQ -->|Yes| HealthType{"Health Check
Type?"} + HealthType -->|Simple| SimpleHealth["Basic Health Checks"] + HealthType -->|Complex| ComplexHealth["Dependency Health Checks"] + + Counters --> Collection["Metrics Collection"] + Histograms --> Collection + Gauges --> Collection + + AppTracing --> TracingCollection["Trace Collection"] + DistTracing --> TracingCollection + + SimpleHealth --> HealthCollection["Health Monitoring"] + ComplexHealth --> HealthCollection + + Collection --> Export["Export & Integration"] + TracingCollection --> Export + HealthCollection --> Export + + Export --> Production["Production Observability"] + + style Start fill:#4da6ff,stroke:#0066cc,color:white + style Counters fill:#4dbb5f,stroke:#36873f,color:white + style AppTracing fill:#ffa64d,stroke:#cc7a30,color:white + style SimpleHealth fill:#d94dbb,stroke:#a3378a,color:white +``` + +## ๐ŸŽฏ METRICS COLLECTION PATTERNS + +### Lock-Free Metrics for High Performance +```rust +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Arc; +use dashmap::DashMap; +use prometheus::{Counter, Histogram, Gauge, Registry, Opts, HistogramOpts}; + +// โœ… Lock-free atomic counter for high-throughput scenarios +#[derive(Debug)] +pub struct AtomicCounter { + value: AtomicU64, +} + +impl AtomicCounter { + pub fn new() -> Self { + Self { + value: AtomicU64::new(0), + } + } + + pub fn increment(&self) -> u64 { + self.value.fetch_add(1, Ordering::Relaxed) + } + + pub fn add(&self, value: u64) -> u64 { + self.value.fetch_add(value, Ordering::Relaxed) + } + + pub fn get(&self) -> u64 { + self.value.load(Ordering::Relaxed) + } + + pub fn reset(&self) -> u64 { + self.value.swap(0, Ordering::Relaxed) + } +} + +impl Default for AtomicCounter { + fn default() -> Self { + Self::new() + } +} + +// โœ… Comprehensive metrics collector +pub struct MetricsCollector { + counters: DashMap>, + prometheus_counters: DashMap, + prometheus_histograms: DashMap, + prometheus_gauges: DashMap, + registry: Registry, +} + +impl MetricsCollector { + pub fn new() -> Self { + Self { + counters: DashMap::new(), + prometheus_counters: DashMap::new(), + prometheus_histograms: DashMap::new(), + prometheus_gauges: DashMap::new(), + registry: Registry::new(), + } + } + + /// Get or create a counter + pub fn counter(&self, name: &str) -> Arc { + self.counters + .entry(name.to_string()) + .or_insert_with(|| Arc::new(AtomicCounter::new())) + .clone() + } + + /// Get or create a Prometheus counter + pub fn prometheus_counter(&self, name: &str, help: &str) -> Result { + if let Some(counter) = self.prometheus_counters.get(name) { + return Ok(counter.clone()); + } + + let opts = Opts::new(name, help); + let counter = Counter::with_opts(opts)?; + self.registry.register(Box::new(counter.clone()))?; + self.prometheus_counters.insert(name.to_string(), counter.clone()); + + Ok(counter) + } + + /// Get or create a Prometheus histogram + pub fn prometheus_histogram(&self, name: &str, help: &str, buckets: Vec) -> Result { + if let Some(histogram) = self.prometheus_histograms.get(name) { + return Ok(histogram.clone()); + } + + let opts = HistogramOpts::new(name, help).buckets(buckets); + let histogram = Histogram::with_opts(opts)?; + self.registry.register(Box::new(histogram.clone()))?; + self.prometheus_histograms.insert(name.to_string(), histogram.clone()); + + Ok(histogram) + } + + /// Get or create a Prometheus gauge + pub fn prometheus_gauge(&self, name: &str, help: &str) -> Result { + if let Some(gauge) = self.prometheus_gauges.get(name) { + return Ok(gauge.clone()); + } + + let opts = Opts::new(name, help); + let gauge = Gauge::with_opts(opts)?; + self.registry.register(Box::new(gauge.clone()))?; + self.prometheus_gauges.insert(name.to_string(), gauge.clone()); + + Ok(gauge) + } + + /// Export metrics in Prometheus format + pub fn export_prometheus(&self) -> Result { + use prometheus::Encoder; + let encoder = prometheus::TextEncoder::new(); + let metric_families = self.registry.gather(); + + let mut buffer = Vec::new(); + encoder.encode(&metric_families, &mut buffer)?; + + Ok(String::from_utf8(buffer)?) + } + + /// Get all counter values as a snapshot + pub fn counter_snapshot(&self) -> std::collections::HashMap { + self.counters + .iter() + .map(|entry| (entry.key().clone(), entry.value().get())) + .collect() + } +} + +impl Default for MetricsCollector { + fn default() -> Self { + Self::new() + } +} +``` + +### Application Metrics Patterns +```rust +use std::time::{Duration, Instant}; + +// โœ… Request timing middleware pattern +pub struct RequestTimer { + start_time: Instant, + metrics: Arc, + operation: String, +} + +impl RequestTimer { + pub fn start(metrics: Arc, operation: impl Into) -> Self { + let operation = operation.into(); + + // Increment request counter + metrics.counter(&format!("{}_requests_total", operation)).increment(); + + Self { + start_time: Instant::now(), + metrics, + operation, + } + } + + pub fn finish(self) -> Duration { + let duration = self.start_time.elapsed(); + + // Record timing histogram + if let Ok(histogram) = self.metrics.prometheus_histogram( + &format!("{}_duration_seconds", self.operation), + &format!("Request duration for {}", self.operation), + vec![0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0], + ) { + histogram.observe(duration.as_secs_f64()); + } + + duration + } +} + +// โœ… Metrics middleware for HTTP servers +#[derive(Clone)] +pub struct MetricsMiddleware { + metrics: Arc, +} + +impl MetricsMiddleware { + pub fn new(metrics: Arc) -> Self { + Self { metrics } + } + + pub async fn track_request(&self, operation: &str, future: F) -> T + where + F: FnOnce() -> Fut, + Fut: std::future::Future, + { + let timer = RequestTimer::start(self.metrics.clone(), operation); + let result = future().await; + timer.finish(); + result + } +} + +// โœ… Business metrics collection +pub trait BusinessMetrics { + fn record_user_registration(&self); + fn record_user_login(&self); + fn record_transaction(&self, amount: f64); + fn record_error(&self, error_type: &str); +} + +impl BusinessMetrics for MetricsCollector { + fn record_user_registration(&self) { + self.counter("user_registrations_total").increment(); + } + + fn record_user_login(&self) { + self.counter("user_logins_total").increment(); + } + + fn record_transaction(&self, amount: f64) { + self.counter("transactions_total").increment(); + + if let Ok(histogram) = self.prometheus_histogram( + "transaction_amounts", + "Distribution of transaction amounts", + vec![1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0, 1000.0], + ) { + histogram.observe(amount); + } + } + + fn record_error(&self, error_type: &str) { + self.counter(&format!("errors_total_{}", error_type)).increment(); + } +} +``` + +## ๐Ÿ” DISTRIBUTED TRACING PATTERNS + +### OpenTelemetry Integration +```rust +use opentelemetry::global; +use opentelemetry::trace::{TraceId, SpanId, TraceContextExt, Tracer}; +use opentelemetry::Context; +use tracing::{info_span, Span}; +use tracing_opentelemetry::OpenTelemetrySpanExt; + +// โœ… Tracing configuration +pub struct TracingConfig { + pub service_name: String, + pub service_version: String, + pub environment: String, + pub jaeger_endpoint: Option, +} + +pub fn setup_tracing(config: TracingConfig) -> Result<(), TracingError> { + use opentelemetry::sdk::trace; + use opentelemetry::sdk::Resource; + use opentelemetry::KeyValue; + + // Create resource with service information + let resource = Resource::new(vec![ + KeyValue::new("service.name", config.service_name), + KeyValue::new("service.version", config.service_version), + KeyValue::new("environment", config.environment), + ]); + + // Initialize tracer + let tracer = if let Some(endpoint) = config.jaeger_endpoint { + opentelemetry_jaeger::new_agent_pipeline() + .with_endpoint(endpoint) + .with_service_name("my-service") + .install_simple()? + } else { + opentelemetry::sdk::trace::TracerProvider::builder() + .with_config(trace::config().with_resource(resource)) + .build() + .tracer("my-service") + }; + + // Initialize tracing subscriber + let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); + + use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + tracing_subscriber::registry() + .with(telemetry) + .with(tracing_subscriber::fmt::layer()) + .with(tracing_subscriber::EnvFilter::from_default_env()) + .init(); + + Ok(()) +} + +// โœ… Tracing helper macros and functions +pub fn trace_async_fn(span_name: &str, future: F) -> impl std::future::Future +where + F: FnOnce() -> Fut, + Fut: std::future::Future, +{ + async move { + let span = info_span!("async_operation", operation = span_name); + let _enter = span.enter(); + future().await + } +} + +// โœ… Distributed tracing context propagation +pub struct TraceContext { + pub trace_id: TraceId, + pub span_id: SpanId, + pub span_context: opentelemetry::trace::SpanContext, +} + +impl TraceContext { + pub fn current() -> Option { + let context = Context::current(); + let span_context = context.span().span_context(); + + if span_context.is_valid() { + Some(Self { + trace_id: span_context.trace_id(), + span_id: span_context.span_id(), + span_context: span_context.clone(), + }) + } else { + None + } + } + + pub fn to_headers(&self) -> std::collections::HashMap { + use opentelemetry::propagation::{Injector, TextMapPropagator}; + + let mut headers = std::collections::HashMap::new(); + let propagator = opentelemetry_jaeger::Propagator::new(); + let context = Context::current_with_span( + opentelemetry::trace::TraceContextExt::span(&Context::current()) + ); + + propagator.inject_context(&context, &mut headers); + headers + } + + pub fn from_headers(headers: &std::collections::HashMap) -> Context { + use opentelemetry::propagation::{Extractor, TextMapPropagator}; + + let propagator = opentelemetry_jaeger::Propagator::new(); + let context = propagator.extract(headers); + context + } +} + +// โœ… Custom span attributes +pub trait SpanExtensions { + fn with_user_id(self, user_id: &str) -> Self; + fn with_request_id(self, request_id: &str) -> Self; + fn with_operation_result(self, success: bool) -> Self; +} + +impl SpanExtensions for Span { + fn with_user_id(self, user_id: &str) -> Self { + self.record("user.id", user_id); + self + } + + fn with_request_id(self, request_id: &str) -> Self { + self.record("request.id", request_id); + self + } + + fn with_operation_result(self, success: bool) -> Self { + self.record("operation.success", success); + if !success { + self.record("error", true); + } + self + } +} +``` + +## ๐Ÿฅ HEALTH CHECK PATTERNS + +### Comprehensive Health Monitoring +```rust +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::time::Duration; + +// โœ… Health check status +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum HealthStatus { + Healthy, + Degraded, + Unhealthy, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct HealthCheckResult { + pub status: HealthStatus, + pub message: String, + pub details: HashMap, + pub duration: Duration, + pub timestamp: chrono::DateTime, +} + +impl HealthCheckResult { + pub fn healthy(message: impl Into) -> Self { + Self { + status: HealthStatus::Healthy, + message: message.into(), + details: HashMap::new(), + duration: Duration::from_millis(0), + timestamp: chrono::Utc::now(), + } + } + + pub fn degraded(message: impl Into) -> Self { + Self { + status: HealthStatus::Degraded, + message: message.into(), + details: HashMap::new(), + duration: Duration::from_millis(0), + timestamp: chrono::Utc::now(), + } + } + + pub fn unhealthy(message: impl Into) -> Self { + Self { + status: HealthStatus::Unhealthy, + message: message.into(), + details: HashMap::new(), + duration: Duration::from_millis(0), + timestamp: chrono::Utc::now(), + } + } + + pub fn with_detail(mut self, key: impl Into, value: serde_json::Value) -> Self { + self.details.insert(key.into(), value); + self + } + + pub fn with_duration(mut self, duration: Duration) -> Self { + self.duration = duration; + self + } +} + +// โœ… Health check trait +#[async_trait::async_trait] +pub trait HealthCheck: Send + Sync { + async fn check(&self) -> HealthCheckResult; + fn name(&self) -> &str; + fn timeout(&self) -> Duration { + Duration::from_secs(5) + } +} + +// โœ… Database health check implementation +pub struct DatabaseHealthCheck { + pool: sqlx::PgPool, + name: String, +} + +impl DatabaseHealthCheck { + pub fn new(pool: sqlx::PgPool, name: impl Into) -> Self { + Self { + pool, + name: name.into(), + } + } +} + +#[async_trait::async_trait] +impl HealthCheck for DatabaseHealthCheck { + async fn check(&self) -> HealthCheckResult { + let start = std::time::Instant::now(); + + match sqlx::query("SELECT 1").fetch_one(&self.pool).await { + Ok(_) => { + let duration = start.elapsed(); + HealthCheckResult::healthy("Database connection successful") + .with_duration(duration) + .with_detail("query_time_ms", (duration.as_millis() as f64).into()) + } + Err(e) => { + let duration = start.elapsed(); + HealthCheckResult::unhealthy(format!("Database connection failed: {}", e)) + .with_duration(duration) + .with_detail("error", e.to_string().into()) + } + } + } + + fn name(&self) -> &str { + &self.name + } + + fn timeout(&self) -> Duration { + Duration::from_secs(10) + } +} + +// โœ… Health check manager +pub struct HealthManager { + checks: HashMap>, +} + +impl HealthManager { + pub fn new() -> Self { + Self { + checks: HashMap::new(), + } + } + + pub fn add_check(&mut self, check: Box) { + let name = check.name().to_string(); + self.checks.insert(name, check); + } + + pub async fn check_all(&self) -> HashMap { + let mut results = HashMap::new(); + + for (name, check) in &self.checks { + let timeout = check.timeout(); + + let result = match tokio::time::timeout(timeout, check.check()).await { + Ok(result) => result, + Err(_) => HealthCheckResult::unhealthy(format!("Health check timed out after {:?}", timeout)) + .with_detail("timeout", timeout.as_secs().into()), + }; + + results.insert(name.clone(), result); + } + + results + } + + pub async fn overall_status(&self) -> HealthStatus { + let results = self.check_all().await; + + let mut has_degraded = false; + + for result in results.values() { + match result.status { + HealthStatus::Unhealthy => return HealthStatus::Unhealthy, + HealthStatus::Degraded => has_degraded = true, + HealthStatus::Healthy => {} + } + } + + if has_degraded { + HealthStatus::Degraded + } else { + HealthStatus::Healthy + } + } +} + +impl Default for HealthManager { + fn default() -> Self { + Self::new() + } +} +``` + +## ๐Ÿšจ ERROR HANDLING FOR OBSERVABILITY + +### Observability Error Types +```rust +#[derive(Debug, thiserror::Error)] +pub enum MetricsError { + #[error("Prometheus error: {0}")] + Prometheus(#[from] prometheus::Error), + + #[error("UTF-8 encoding error: {0}")] + Utf8(#[from] std::string::FromUtf8Error), + + #[error("Metrics collection failed: {0}")] + Collection(String), +} + +#[derive(Debug, thiserror::Error)] +pub enum TracingError { + #[error("OpenTelemetry error: {0}")] + OpenTelemetry(#[from] opentelemetry::trace::TraceError), + + #[error("Jaeger error: {0}")] + Jaeger(String), + + #[error("Tracing setup failed: {0}")] + Setup(String), +} + +#[derive(Debug, thiserror::Error)] +pub enum HealthError { + #[error("Health check failed: {0}")] + CheckFailed(String), + + #[error("Health check timeout: {0}")] + Timeout(String), + + #[error("Health manager error: {0}")] + Manager(String), +} +``` + +## ๐Ÿงช OBSERVABILITY TESTING + +### Testing Patterns +```rust +#[cfg(test)] +mod tests { + use super::*; + use std::time::Duration; + + #[test] + fn test_atomic_counter() { + let counter = AtomicCounter::new(); + assert_eq!(counter.get(), 0); + + counter.increment(); + assert_eq!(counter.get(), 1); + + counter.add(5); + assert_eq!(counter.get(), 6); + + let old_value = counter.reset(); + assert_eq!(old_value, 6); + assert_eq!(counter.get(), 0); + } + + #[tokio::test] + async fn test_metrics_collector() { + let collector = MetricsCollector::new(); + + // Test counter creation and increment + let counter = collector.counter("test_requests"); + counter.increment(); + counter.add(5); + + let snapshot = collector.counter_snapshot(); + assert_eq!(snapshot.get("test_requests"), Some(&6)); + } + + #[tokio::test] + async fn test_request_timer() { + let metrics = Arc::new(MetricsCollector::new()); + let timer = RequestTimer::start(metrics.clone(), "test_operation"); + + // Simulate some work + tokio::time::sleep(Duration::from_millis(10)).await; + + let duration = timer.finish(); + assert!(duration >= Duration::from_millis(10)); + + // Check that counter was incremented + let snapshot = metrics.counter_snapshot(); + assert_eq!(snapshot.get("test_operation_requests_total"), Some(&1)); + } + + #[tokio::test] + async fn test_health_check() { + struct MockHealthCheck { + should_pass: bool, + } + + #[async_trait::async_trait] + impl HealthCheck for MockHealthCheck { + async fn check(&self) -> HealthCheckResult { + if self.should_pass { + HealthCheckResult::healthy("Mock check passed") + } else { + HealthCheckResult::unhealthy("Mock check failed") + } + } + + fn name(&self) -> &str { + "mock_check" + } + } + + let mut manager = HealthManager::new(); + manager.add_check(Box::new(MockHealthCheck { should_pass: true })); + + let results = manager.check_all().await; + assert_eq!(results.len(), 1); + assert_eq!(results["mock_check"].status, HealthStatus::Healthy); + + let overall = manager.overall_status().await; + assert_eq!(overall, HealthStatus::Healthy); + } +} +``` + +## โœ… OBSERVABILITY CHECKLIST + +```markdown +### Observability Implementation Verification +- [ ] Lock-free metrics collection implemented +- [ ] Prometheus metrics integration working +- [ ] Request timing and duration tracking +- [ ] Business metrics collection in place +- [ ] Distributed tracing configured +- [ ] Trace context propagation working +- [ ] Custom span attributes defined +- [ ] Health checks for all dependencies +- [ ] Health check timeout handling +- [ ] Overall health status aggregation +- [ ] Metrics export endpoints exposed +- [ ] Error metrics collection +- [ ] Performance impact minimized +- [ ] Testing coverage for all components +- [ ] Documentation for metrics and traces +``` + +This observability guide provides comprehensive patterns for monitoring, tracing, and health checking in production Rust applications. diff --git a/.cursor/rules/rust/main.mdc b/.cursor/rules/rust/main.mdc index 538f497..be2cd94 100644 --- a/.cursor/rules/rust/main.mdc +++ b/.cursor/rules/rust/main.mdc @@ -38,6 +38,8 @@ graph TD Features --> GRPC{"gRPC/Protobuf
needed?"} Features --> Concurrent{"Heavy
concurrency?"} Features --> Config{"Complex config
or templating?"} + Features --> Observability{"Metrics & tracing
needed?"} + Features --> ConfigMgmt{"Dynamic config
management?"} Web -->|Yes| WebRules["Load Axum Rules"] DB -->|Yes| DBRules["Load Database Rules"] @@ -45,6 +47,8 @@ graph TD GRPC -->|Yes| GRPCRules["Load Protobuf & gRPC Rules"] Concurrent -->|Yes| ConcurrencyRules["Load Concurrency Rules"] Config -->|Yes| ToolsRules["Load Tools & Config Rules"] + Observability -->|Yes| ObservabilityRules["Load Observability Rules"] + ConfigMgmt -->|Yes| ConfigMgmtRules["Load Configuration Rules"] style Start fill:#4da6ff,stroke:#0066cc,color:white style Complex fill:#d94dbb,stroke:#a3378a,color:white @@ -100,6 +104,8 @@ graph TD FeatureDetection --> GRPCFeature{"gRPC/Protobuf?"} FeatureDetection --> SerdeFeature{"Serialization?"} FeatureDetection --> BuilderFeature{"Complex Types?"} + FeatureDetection --> ObservabilityFeature{"Metrics & Tracing?"} + FeatureDetection --> ConfigFeature{"Dynamic Config?"} WebFeature -->|Yes| AxumRules["Axum Framework Rules"] DBFeature -->|Yes| SQLxRules["SQLx Database Rules"] @@ -107,6 +113,8 @@ graph TD GRPCFeature -->|Yes| GRPCRules["Protobuf & gRPC Rules"] SerdeFeature -->|Yes| SerdeRules["Serde Best Practices"] BuilderFeature -->|Yes| TypedBuilderRules["TypedBuilder Rules"] + ObservabilityFeature -->|Yes| ObservabilityRules["Observability Rules"] + ConfigFeature -->|Yes| ConfigurationRules["Configuration Rules"] style Main fill:#4da6ff,stroke:#0066cc,color:white style Core fill:#ffa64d,stroke:#cc7a30,color:white @@ -183,6 +191,22 @@ sequenceDiagram - [ ] Background task processing - [ ] โ†’ Load Concurrency rules if YES to any +### Configuration Management Requirements +- [ ] Dynamic configuration reloading needed +- [ ] Multi-format config support (YAML/TOML/JSON) +- [ ] Configuration validation required +- [ ] Environment-based configuration overrides +- [ ] File system watching for config changes +- [ ] โ†’ Load Configuration rules if YES to any + +### Observability Requirements +- [ ] Metrics collection needed +- [ ] Performance monitoring required +- [ ] Distributed tracing needed +- [ ] Health checks for dependencies +- [ ] Prometheus integration required +- [ ] โ†’ Load Observability rules if YES to any + ### Tools & Configuration Requirements - [ ] Complex configuration files - [ ] Template rendering needed @@ -253,6 +277,8 @@ Based on project analysis, load specific rule sets: # Database: core + sqlx + utilities (error handling) # CLI: core + cli + utilities (enum_dispatch + error handling) # gRPC: core + protobuf-grpc + utilities (typed_builder + sanitization) +# Config Management: core + configuration + observability (metrics) +# Service Applications: core + configuration + observability + utilities # Auth: core + utilities (JWT + validation) ``` @@ -274,6 +300,8 @@ Based on project analysis, load specific rule sets: | **CLI** | `features/cli.mdc` | Clap 4.0+ patterns, subcommands, enum_dispatch | | **Protobuf & gRPC** | `features/protobuf-grpc.mdc` | Prost/Tonic 0.13+, Inner types, MessageSanitizer, reflection | | **Concurrency** | `features/concurrency.mdc` | Tokio, DashMap, async patterns | +| **Configuration** | `features/configuration.mdc` | Multi-format config, hot-reloading, validation patterns | +| **Observability** | `features/observability.mdc` | Metrics collection, distributed tracing, health checks | | **Tools & Config** | `features/tools-and-config.mdc` | Tracing, YAML config, MiniJinja templates | | **Utilities** | `features/utilities.mdc` | JWT auth, CLI tools, builders, enhanced derives | | **HTTP Client** | `features/http-client.mdc` | reqwest patterns, error handling, retry logic | @@ -295,6 +323,8 @@ Based on project analysis, load specific rule sets: - **Enterprise applications** (multiple domains) - **Database systems** with multiple engines - **Distributed systems** with event processing +- **Production services** (requiring metrics, health checks, config reloading) +- **High-availability applications** (with observability and dynamic configuration) ## ๐Ÿš€ MODERN RUST STACK PREFERENCES @@ -323,7 +353,17 @@ Based on real-world experience, these are the recommended tools: - **Templates**: `minijinja` (never `handlebars`) - **Data Extraction**: `jsonpath-rust` -### Observability +### Configuration Management +- **Multi-format Config**: `figment` with YAML/TOML/JSON support +- **Configuration Validation**: `validator` for compile-time guarantees +- **Hot Reloading**: `arc-swap` + `notify` for atomic updates +- **Environment Overrides**: Always use environment variable precedence + +### Observability & Monitoring +- **Metrics Collection**: `prometheus` + lock-free atomic counters +- **Distributed Tracing**: `opentelemetry` + `tracing-opentelemetry` +- **Health Checks**: Custom health check frameworks with timeout handling +- **Concurrent Metrics**: `dashmap` for lock-free metrics storage - **Logging**: `tracing` + `tracing-subscriber` (never `env_logger`) - **Structured Logging**: Always use `#[instrument]` and context - **Log Rotation**: `tracing-appender` for production diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..fe03378 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +Copyright <2025> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 5521a61..35fdf7a 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,14 @@ A comprehensive collection of Rust development rules designed for use with Curso ## โœจ Features -- ๐Ÿ—๏ธ **Modern Architecture Patterns** - Domain-driven design, subsystem organization, and clean architecture +- ๐Ÿ—๏ธ **Modern Architecture Patterns** - Domain-driven design, service-oriented architecture, and clean architecture - ๐ŸŒ **Web Development Standards** - Axum web framework, OpenAPI integration, authentication patterns - ๐Ÿ—„๏ธ **Database Best Practices** - SQLx patterns, migrations, connection pooling, and repository patterns - โšก **Async & Concurrency** - Tokio patterns, async/await best practices, and concurrent data structures +- โš™๏ธ **Configuration Management** - Multi-format config support, hot-reloading, validation, and environment overrides +- ๐Ÿ“Š **Observability & Monitoring** - Metrics collection, distributed tracing, health checks, and performance monitoring - ๐Ÿ› ๏ธ **Utility Libraries** - JWT authentication, CLI development, HTTP clients, and more -- ๐Ÿ“ฆ **Workspace Organization** - Multi-crate workspace structure with clear subsystem boundaries +- ๐Ÿ“ฆ **Workspace Organization** - Multi-crate workspace structure with clear service boundaries - ๐Ÿ” **Code Quality** - Comprehensive linting, error handling, testing, and documentation standards ## ๐Ÿš€ Quick Start @@ -47,6 +49,8 @@ rules: - "@rust/database" # Database patterns with SQLx - "@rust/cli" # CLI applications with clap - "@rust/concurrency" # Async and concurrency patterns + - "@rust/configuration" # Configuration management patterns + - "@rust/observability" # Metrics, tracing, and health checks - "@rust/utilities" # Utility libraries and CLI tools - "@rust/workspace" # Multi-crate workspace organization ``` @@ -59,6 +63,14 @@ rules: - "@rust/core/code-quality" - "@rust/features/axum" - "@rust/features/database" + - "@rust/features/observability" # For production monitoring + +# .cursorrules - Service with dynamic configuration +rules: + - "@rust/core/code-quality" + - "@rust/features/configuration" + - "@rust/features/observability" + - "@rust/features/utilities" ``` ## ๐Ÿ“ Project Structure @@ -74,6 +86,8 @@ cursor-rust-rules/ โ”‚ โ”‚ โ”œโ”€โ”€ database.mdc # Database patterns โ”‚ โ”‚ โ”œโ”€โ”€ cli.mdc # CLI application patterns โ”‚ โ”‚ โ”œโ”€โ”€ concurrency.mdc # Async/concurrency patterns +โ”‚ โ”‚ โ”œโ”€โ”€ configuration.mdc # Configuration management patterns +โ”‚ โ”‚ โ”œโ”€โ”€ observability.mdc # Metrics, tracing, health checks โ”‚ โ”‚ โ”œโ”€โ”€ utilities.mdc # Utility libraries โ”‚ โ”‚ โ”œโ”€โ”€ http-client.mdc # HTTP client patterns โ”‚ โ”‚ โ””โ”€โ”€ tools-and-config.mdc # Development tools @@ -94,6 +108,8 @@ cursor-rust-rules/ - **Database** (`@rust/features/database`) - SQLx patterns, migrations, connection pooling - **CLI Applications** (`@rust/features/cli`) - Clap 4.0+, subcommands, enum_dispatch, modern CLI patterns - **Concurrency** (`@rust/features/concurrency`) - Tokio, async patterns, concurrent data structures +- **Configuration** (`@rust/features/configuration`) - Multi-format config, hot-reloading, validation patterns +- **Observability** (`@rust/features/observability`) - Metrics collection, distributed tracing, health checks - **Utilities** (`@rust/features/utilities`) - JWT, CLI tools, builder patterns, validation - **HTTP Client** (`@rust/features/http-client`) - Reqwest patterns, retry logic, authentication - **Tools & Config** (`@rust/features/tools-and-config`) - Development tooling and configuration @@ -115,17 +131,30 @@ axum = { version = "0.8", features = ["macros", "multipart", "ws"] } utoipa = { version = "5.0", features = ["axum_extras", "chrono", "uuid"] } sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres"] } tokio = { version = "1.45", features = ["macros", "rt-multi-thread"] } + +# Configuration management +figment = { version = "0.10", features = ["yaml", "toml", "env"] } +arc-swap = "1.0" +validator = { version = "0.18", features = ["derive"] } + +# Observability +prometheus = "0.13" +opentelemetry = "0.23" +tracing-opentelemetry = "0.23" +dashmap = "6.0" ``` Your project follows patterns like: ```rust -// AppState with hot-reloadable config +// AppState with hot-reloadable config and observability #[derive(Clone)] pub struct AppState { pub config: Arc>, pub db: PgPool, pub http_client: reqwest::Client, + pub metrics: Arc, + pub health_manager: Arc, } // Route handlers with OpenAPI documentation @@ -172,6 +201,85 @@ pub async fn setup_database(config: &DatabaseConfig) -> Result { + current: Arc>, + reload_tx: broadcast::Sender>, +} + +impl ConfigManager +where + T: for<'de> Deserialize<'de> + Validate + Clone + Send + Sync + 'static, +{ + pub async fn start_watching(&self) -> Result<(), ConfigError> { + // File system watching with atomic configuration updates + // Supports YAML, TOML, JSON with environment variable overrides + } +} +``` + +### Observability & Monitoring + +For production-ready metrics and health checks: + +```rust +// Lock-free metrics collection +pub struct MetricsCollector { + counters: DashMap>, + prometheus_registry: Registry, +} + +// Request timing middleware +pub struct RequestTimer { + start_time: Instant, + metrics: Arc, + operation: String, +} + +// Health check framework +#[async_trait] +pub trait HealthCheck: Send + Sync { + async fn check(&self) -> HealthCheckResult; + fn name(&self) -> &str; +} + +// Distributed tracing setup +pub fn setup_tracing(config: TracingConfig) -> Result<(), TracingError> { + let tracer = opentelemetry_jaeger::new_agent_pipeline() + .with_service_name(&config.service_name) + .install_simple()?; + + tracing_subscriber::registry() + .with(tracing_opentelemetry::layer().with_tracer(tracer)) + .with(tracing_subscriber::fmt::layer()) + .init(); + + Ok(()) +} +``` + ### CLI Application Development For building powerful command-line tools: @@ -220,22 +328,27 @@ impl CommandExecutor for DatabaseCommand { ### Multi-Crate Workspace -For complex applications, organize as subsystems: +For complex applications, organize as services: ``` -my-ecommerce-platform/ +my-service-platform/ โ”œโ”€โ”€ shared/ # Cross-cutting infrastructure โ”‚ โ”œโ”€โ”€ shared-types/ # Domain primitives -โ”‚ โ”œโ”€โ”€ shared-db/ # Database infrastructure -โ”‚ โ””โ”€โ”€ shared-events/ # Event definitions -โ”œโ”€โ”€ subsystems/ # Business subsystems -โ”‚ โ”œโ”€โ”€ user-management/ # User & auth subsystem -โ”‚ โ”œโ”€โ”€ product-catalog/ # Product management -โ”‚ โ”œโ”€โ”€ order-management/ # Order processing -โ”‚ โ””โ”€โ”€ payment/ # Payment processing -โ””โ”€โ”€ services/ # Infrastructure services - โ”œโ”€โ”€ api-gateway/ # Unified API - โ””โ”€โ”€ admin-cli/ # Admin tools +โ”‚ โ”œโ”€โ”€ shared-config/ # Configuration management +โ”‚ โ””โ”€โ”€ shared-observability/ # Metrics & tracing +โ”œโ”€โ”€ services/ # Business services +โ”‚ โ”œโ”€โ”€ core-services/ # Core business logic +โ”‚ โ”‚ โ”œโ”€โ”€ service-core/ # Domain logic +โ”‚ โ”‚ โ”œโ”€โ”€ service-api/ # HTTP endpoints +โ”‚ โ”‚ โ””โ”€โ”€ service-worker/ # Background processing +โ”‚ โ””โ”€โ”€ business-services/ # Business-specific services +โ”‚ โ”œโ”€โ”€ business-core/ # Business logic +โ”‚ โ”œโ”€โ”€ business-api/ # API endpoints +โ”‚ โ””โ”€โ”€ business-processor/ # Workflow engine +โ””โ”€โ”€ platform/ # Platform services + โ”œโ”€โ”€ gateway/ # API gateway + โ”œโ”€โ”€ admin-panel/ # Administration + โ””โ”€โ”€ health-monitor/ # Health monitoring ``` ## ๐ŸŽฎ Interactive Features @@ -255,6 +368,15 @@ async fn my_function() -> Result<(), Error> { // Using database patterns triggers SQLx suggestions sqlx::query!("SELECT * FROM users") // Cursor suggests: Add sqlx with postgres features + +// Using configuration patterns triggers config dependencies +#[derive(Deserialize, Validate)] +struct Config { /* ... */ } + // Cursor suggests: Add figment, validator, arc-swap + +// Using metrics patterns triggers observability dependencies +let counter = metrics.counter("requests_total"); + // Cursor suggests: Add prometheus, dashmap, opentelemetry ``` ### Smart Code Generation @@ -271,8 +393,9 @@ Rules include templates for common patterns: Rules are loaded based on project complexity: 1. **Simple Projects** - Core quality standards only -2. **Web APIs** - Core + Axum + Database rules -3. **Complex Systems** - All rules including workspace organization +2. **Web APIs** - Core + Axum + Database + Observability rules +3. **Service Applications** - Core + Configuration + Observability + Utilities +4. **Complex Systems** - All rules including workspace organization ## ๐Ÿ”ง Customization diff --git a/specs/instructions.md b/specs/instructions.md index c173b14..e344003 100644 --- a/specs/instructions.md +++ b/specs/instructions.md @@ -179,3 +179,7 @@ async pub fn say_hello( 5. @/features ่ง„ๅˆ™ๆ˜ฏๅˆ็†็š„๏ผŒไธ”ๅˆฉไบŽไบง็”Ÿ้ซ˜่ดจ้‡ไธ”็ฎ€ๆด็š„ไปฃ็  6. ่ฟ™ไบ› rules ๆ— ่ฎบๅฏนๆ–ฐ้กน็›ฎ่ฟ˜ๆ˜ฏ่€้กน็›ฎ้ƒฝ่ƒฝๅคŸไบง็”Ÿ้ซ˜่ดจ้‡ไธ”็ฎ€ๆด็š„ไปฃ็  ๅฆ‚ๆžœไฝ ๅ‘็Žฐ้œ€่ฆไปปไฝ•ไฟฎๆ”น๏ผŒ่ฏทๅœจ ./spec/ ไธ‹็”Ÿๆˆไธ€ไธช่ฏฆ็ป†็š„ไฟฎๆ”น่ฎกๅˆ’ + +่ฏทไป”็ป†้˜…่ฏป @/rust rule sets๏ผŒ ็„ถๆ นๆฎไธ‹้ขๅˆ—ๅ‡บ็š„ prompt๏ผŒๆ‰พๅ‡บ common best practice๏ผŒๅœจ specs ็›ฎๅฝ•ไธ‹็”Ÿๆˆไธ€ไธชไฝ ๆƒณๅฆ‚ไฝ•ๆ›ดๆ–ฐ rust rules ็š„่ฏฆ็ป†่ฎกๅˆ’ใ€‚ๆณจๆ„๏ผŒๅฆ‚ๆžœไฝ ่ฆๆทปๅŠ ๆ–ฐ็š„ไปฃ็ ็คบไพ‹๏ผŒ่ฏท็กฎไฟๅฎƒๆ˜ฏ่ถณๅคŸ common ็š„ใ€‚ๅฆ‚ๆžœๆ นๆฎไปฅไธ‹ๅ†…ๅฎนไฝ ่ง‰ๅพ—้œ€่ฆๆ–ฐๅปบไธ€ไธช pingora ็š„ rules๏ผŒ่ฏทไนŸๅœจๆ–ฐ็š„่ฎกๅˆ’ๆ–‡ไปถไธญ่ฏฆ็ป†่ฏดๆ˜Žใ€‚ + +่ฏทๆ›ดๆ–ฐ @rust-rules-improvement-plan.md ้‡Œ็š„ไปฃ็ ่ถณๅคŸ common๏ผŒไธ่ฆๅŒ…ๅซ็‰นๅฎš็š„ๅ†…ๅฎน๏ผŒ่€Œไธ”ๅ†…ๅฎน้œ€่ฆไฟๆŒๆญฃไบค๏ผˆhot-reloading ๅ‡บ็Žฐไธ€ๆฌกๅฐฑๅฏไปฅไบ†๏ผ‰ใ€‚ไน‹ๅŽ๏ผŒ่ฏทๆŒ‰็…ง่ฎกๅˆ’ๆ›ดๆ–ฐๆˆ–่€…็”Ÿๆˆๆ–ฐ็š„ rulesใ€‚ไธๅฟ…ๆ›ดๆ–ฐ performance.mdc๏ผŒไธๅฟ…ๆ›ดๆ–ฐ api-design.mdc๏ผŒไนŸไธๅฟ…ๆทปๅŠ  pingora.mdc diff --git a/specs/rust-rules-improvement-plan.md b/specs/rust-rules-improvement-plan.md new file mode 100644 index 0000000..1b88942 --- /dev/null +++ b/specs/rust-rules-improvement-plan.md @@ -0,0 +1,221 @@ +# Rust Rules Improvement Plan + +## Executive Summary + +After analyzing the existing Rust rule sets and extensive real-world usage patterns, I've identified several common patterns and gaps that warrant updates to our Rust coding standards. This plan outlines specific improvements to existing rules and proposes new rule sets for configuration management and observability. + +## Current Rule Analysis + +### Strengths of Existing Rules + +- **Comprehensive coverage**: Core areas (code quality, dependencies, design patterns, performance) are well-covered +- **Practical examples**: Good balance of theoretical guidance and concrete code examples +- **Workspace architecture**: Excellent multi-crate organization guidance +- **Modern Rust idioms**: Rules emphasize Rust 2024 edition and best practices + +### Identified Gaps from Real-World Usage + +From analyzing common patterns in modern Rust applications, several areas need better coverage: + +1. **Configuration Management** - Multi-format config parsing, validation, and runtime updates +2. **Observability Patterns** - Real-time metrics collection and distributed tracing +3. **Service Architecture** - Async service orchestration and middleware patterns +4. **Frontend Integration** - Embedded web assets and API integration patterns + +## Proposed Rule Updates + +### 1. Enhanced Workspace Architecture Rules + +**File**: `.cursor/rules/rust/complex/workspace.mdc` + +**Changes**: + +- Add **service-oriented architecture** patterns +- Include **plugin architecture** examples for extensible systems +- Add **configuration hot-reloading** patterns + +**New Example Pattern**: + +```rust +// Service-oriented architecture with shared state +pub struct ServiceManager { + config: Arc>, + components: Vec>, +} + +impl ServiceManager { + pub fn update_config(&self, new_config: T) { + self.config.store(Arc::new(new_config)); + // Notify components of config change + for component in &self.components { + component.on_config_update(); + } + } +} + +pub trait ServiceComponent: Send + Sync { + fn on_config_update(&self); +} +``` + +### 2. New Configuration Management Rules + +**New File**: `.cursor/rules/rust/features/configuration.mdc` + +**Purpose**: Comprehensive configuration management patterns + +**Key Sections**: + +- **Multi-format parsing** (YAML, TOML, JSON) +- **Configuration validation** with compile-time guarantees +- **Environment-based overrides** +- **Configuration watching** with filesystem events + +**Example Pattern**: + +```rust +#[derive(Debug, Clone, Deserialize, Validate)] +pub struct AppConfig { + #[validate(length(min = 1))] + pub name: String, + + #[validate(range(min = 1, max = 65535))] + pub port: u16, + + #[serde(default)] + pub features: Vec, +} + +pub struct ConfigManager { + current: Arc>, + reload_tx: mpsc::Sender, +} +``` + +### 3. New Observability Rules + +**New File**: `.cursor/rules/rust/features/observability.mdc` + +**Purpose**: Real-time metrics collection and observability patterns + +**Key Patterns**: + +- **Lock-free counter implementation** for high-throughput scenarios +- **Metrics collection** with proper aggregation +- **Distributed tracing** patterns +- **Health check implementations** + +**Example**: + +```rust +// Lock-free metrics for high-performance scenarios +pub struct AtomicCounter { + value: AtomicU64, +} + +impl AtomicCounter { + pub fn increment(&self) -> u64 { + self.value.fetch_add(1, Ordering::Relaxed) + } + + pub fn get(&self) -> u64 { + self.value.load(Ordering::Relaxed) + } +} + +// Metrics collection pattern +pub struct MetricsCollector { + counters: DashMap, + histograms: DashMap, +} +``` + +### 4. Updated Dependency Management Rules + +**File**: `.cursor/rules/rust/core/dependencies.mdc` + +**Additions**: + +- **Configuration management** dependencies +- **Observability** dependency patterns +- **Frontend integration** dependencies + +**New Section**: Common Service Dependencies + +```toml +# Configuration management +figment = { version = "0.10", features = ["yaml", "toml", "env"] } +notify = "6.0" # File watching +arc-swap = "1.0" # Atomic updates +validator = { version = "0.18", features = ["derive"] } + +# Observability +prometheus = "0.13" +opentelemetry = "0.23" +tracing-opentelemetry = "0.23" +dashmap = "6.0" # Concurrent collections + +# Frontend integration +rust-embed = "8.0" # Static asset embedding +mime_guess = "2.0" # MIME type detection +``` + +## Implementation Priority + +### Phase 1: Critical Updates (High Impact, Common Patterns) + +1. **Configuration Management Rules** - Most frequently used pattern +2. **Observability Rules** - Essential for production systems +3. **Enhanced Workspace Architecture** - Service-oriented patterns + +### Phase 2: Refinements (Medium Impact) + +1. **Updated Dependency Management** - New common dependencies +2. **Documentation Updates** - Ensure all examples are current + +## Common Best Practices Identified + +From analyzing real-world usage, these patterns appear frequently and should be emphasized: + +### 1. Configuration Management + +- **Multi-format support** with validation +- **Environment-based overrides** +- **Atomic configuration updates** using `arc-swap` + +### 2. Observability + +- **Lock-free data structures** for metrics +- **Structured logging** with context +- **Health check patterns** for services + +### 3. Service Architecture + +- **Trait-based components** for extensibility +- **Shared state management** with atomic updates +- **Graceful shutdown** with signal handling + +### 4. Error Handling + +- **Contextual errors** with detailed information +- **Error propagation** patterns +- **Retry patterns** with exponential backoff + +### 5. Testing Strategies + +- **Integration testing** with real services +- **Configuration testing** with multiple formats +- **Metrics testing** with mock collectors + +## Success Metrics + +To measure the effectiveness of these rule updates: + +1. **Developer Velocity**: Faster onboarding for Rust projects +2. **Code Quality**: Reduced bugs in configuration and observability code +3. **Consistency**: More uniform patterns across projects +4. **Maintainability**: Better separation of concerns and testability + +## Conclusion + +These rule updates codify common patterns that have emerged from real-world Rust development, particularly in service-oriented applications. The focus on configuration management and observability addresses critical needs in modern applications while maintaining the high standards of the existing rule set.