373 lines
8 KiB
Markdown
373 lines
8 KiB
Markdown
|
|
# Veza Common Library
|
||
|
|
|
||
|
|
Bibliothèque commune pour tous les services Veza. Cette bibliothèque fournit des types partagés, des utilitaires et des configurations communes utilisées par tous les services du projet Veza.
|
||
|
|
|
||
|
|
## Installation
|
||
|
|
|
||
|
|
Ajoutez cette dépendance à votre `Cargo.toml`:
|
||
|
|
|
||
|
|
```toml
|
||
|
|
[dependencies]
|
||
|
|
veza-common = { path = "../veza-common" }
|
||
|
|
```
|
||
|
|
|
||
|
|
## Modules
|
||
|
|
|
||
|
|
### Types (`types`)
|
||
|
|
|
||
|
|
Types de données partagés pour tous les services.
|
||
|
|
|
||
|
|
#### User
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::types::User;
|
||
|
|
|
||
|
|
let user = User::new(1, "john_doe".to_string(), "john@example.com".to_string());
|
||
|
|
assert!(user.validate().is_ok());
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Track
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::types::Track;
|
||
|
|
use uuid::Uuid;
|
||
|
|
|
||
|
|
let track_id = Uuid::new_v4();
|
||
|
|
let track = Track::new(
|
||
|
|
track_id,
|
||
|
|
"Song Title".to_string(),
|
||
|
|
"Artist Name".to_string(),
|
||
|
|
180 // duration in seconds
|
||
|
|
);
|
||
|
|
assert_eq!(track.formatted_duration(), "3:00");
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Playlist
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::types::Playlist;
|
||
|
|
use uuid::Uuid;
|
||
|
|
|
||
|
|
let playlist_id = Uuid::new_v4();
|
||
|
|
let owner_id = Uuid::new_v4();
|
||
|
|
let mut playlist = Playlist::new(playlist_id, "My Playlist".to_string(), owner_id);
|
||
|
|
|
||
|
|
let track_id = Uuid::new_v4();
|
||
|
|
playlist.add_track(track_id);
|
||
|
|
assert_eq!(playlist.track_count(), 1);
|
||
|
|
```
|
||
|
|
|
||
|
|
#### API Response
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::types::ApiResponse;
|
||
|
|
|
||
|
|
// Success response
|
||
|
|
let response = ApiResponse::success("data".to_string());
|
||
|
|
assert!(response.success);
|
||
|
|
|
||
|
|
// Error response
|
||
|
|
let error_response = ApiResponse::<String>::error("Error message".to_string());
|
||
|
|
assert!(!error_response.success);
|
||
|
|
```
|
||
|
|
|
||
|
|
### Error Handling (`error`)
|
||
|
|
|
||
|
|
Types d'erreurs standardisés avec codes HTTP et messages.
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::error::{CommonError, CommonResult};
|
||
|
|
|
||
|
|
fn example_function() -> CommonResult<i32> {
|
||
|
|
// Return success
|
||
|
|
Ok(42)
|
||
|
|
|
||
|
|
// Or error
|
||
|
|
// Err(CommonError::NotFound("Resource not found".to_string()))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Error codes
|
||
|
|
let error = CommonError::ValidationError("Invalid input".to_string());
|
||
|
|
assert_eq!(error.code(), "VALIDATION_ERROR");
|
||
|
|
assert_eq!(error.http_status_code(), 400);
|
||
|
|
```
|
||
|
|
|
||
|
|
### Configuration (`config`)
|
||
|
|
|
||
|
|
Types de configuration pour Database et Redis.
|
||
|
|
|
||
|
|
#### DatabaseConfig
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::config::DatabaseConfig;
|
||
|
|
use std::time::Duration;
|
||
|
|
|
||
|
|
let config = DatabaseConfig::new(
|
||
|
|
"postgresql://user:password@localhost:5432/mydb".to_string(),
|
||
|
|
10 // max_connections
|
||
|
|
);
|
||
|
|
|
||
|
|
// Validate configuration
|
||
|
|
assert!(config.validate().is_ok());
|
||
|
|
|
||
|
|
// Extract information
|
||
|
|
let host = config.host(); // Some("localhost")
|
||
|
|
let port = config.port(); // Some(5432)
|
||
|
|
let db_name = config.database_name(); // Some("mydb")
|
||
|
|
```
|
||
|
|
|
||
|
|
#### RedisConfig
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::config::RedisConfig;
|
||
|
|
|
||
|
|
let config = RedisConfig::new("redis://localhost:6379".to_string());
|
||
|
|
|
||
|
|
// Validate configuration
|
||
|
|
assert!(config.validate().is_ok());
|
||
|
|
|
||
|
|
// Check SSL
|
||
|
|
let is_ssl = config.is_ssl(); // false for redis://, true for rediss://
|
||
|
|
|
||
|
|
// Extract information
|
||
|
|
let host = config.host(); // Some("localhost")
|
||
|
|
let port = config.port(); // Some(6379)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Utilities (`utils`)
|
||
|
|
|
||
|
|
#### Validation
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::utils::validation::*;
|
||
|
|
|
||
|
|
// Email validation
|
||
|
|
assert!(validate_email("test@example.com"));
|
||
|
|
assert!(!validate_email("invalid-email"));
|
||
|
|
|
||
|
|
// Username validation
|
||
|
|
assert!(validate_username("user123"));
|
||
|
|
assert!(!validate_username("ab")); // Too short
|
||
|
|
|
||
|
|
// Password validation
|
||
|
|
assert!(validate_password("Password123"));
|
||
|
|
assert!(!validate_password("short")); // Too short
|
||
|
|
|
||
|
|
// With Result return
|
||
|
|
let result = validate_email_result("test@example.com");
|
||
|
|
assert!(result.is_ok());
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Serialization
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::utils::serialization::*;
|
||
|
|
use serde::{Serialize, Deserialize};
|
||
|
|
|
||
|
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||
|
|
struct MyStruct {
|
||
|
|
name: String,
|
||
|
|
value: i32,
|
||
|
|
}
|
||
|
|
|
||
|
|
let data = MyStruct {
|
||
|
|
name: "test".to_string(),
|
||
|
|
value: 42,
|
||
|
|
};
|
||
|
|
|
||
|
|
// Serialize to JSON
|
||
|
|
let json = to_json(&data).unwrap();
|
||
|
|
|
||
|
|
// Deserialize from JSON
|
||
|
|
let deserialized: MyStruct = from_json(&json).unwrap();
|
||
|
|
assert_eq!(data, deserialized);
|
||
|
|
|
||
|
|
// Pretty print
|
||
|
|
let pretty = to_json_pretty(&data).unwrap();
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Date Utilities
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::utils::date::*;
|
||
|
|
use chrono::{DateTime, Utc, Duration};
|
||
|
|
|
||
|
|
// Format timestamp
|
||
|
|
let timestamp = 1609459200; // 2021-01-01 00:00:00 UTC
|
||
|
|
let formatted = format_timestamp(timestamp);
|
||
|
|
|
||
|
|
// Parse date
|
||
|
|
let date_str = "2021-01-01T00:00:00Z";
|
||
|
|
let dt: DateTime<Utc> = parse_date(date_str).unwrap();
|
||
|
|
|
||
|
|
// Relative time
|
||
|
|
let now = Utc::now();
|
||
|
|
let past = now - Duration::hours(2);
|
||
|
|
let relative = format_relative_time(&past, &now); // "2 hours ago"
|
||
|
|
|
||
|
|
// Current timestamp
|
||
|
|
let ts = current_timestamp();
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Logging
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::utils::logging::*;
|
||
|
|
use std::collections::HashMap;
|
||
|
|
|
||
|
|
// Simple logging
|
||
|
|
log_info("api", "Service started");
|
||
|
|
log_error("api", "Connection failed", None);
|
||
|
|
|
||
|
|
// Request logging
|
||
|
|
log_request("api", "GET", "/users");
|
||
|
|
|
||
|
|
// Request with context
|
||
|
|
let mut context = HashMap::new();
|
||
|
|
context.insert("user_id".to_string(), "123".to_string());
|
||
|
|
log_request_with_context("api", "POST", "/users", &context);
|
||
|
|
|
||
|
|
// Structured logging
|
||
|
|
let entry = StructuredLogEntry::new("api", "INFO", "User created")
|
||
|
|
.with_context("user_id".to_string(), "123".to_string());
|
||
|
|
let json = entry.to_json();
|
||
|
|
```
|
||
|
|
|
||
|
|
#### General Utilities
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::utils::*;
|
||
|
|
|
||
|
|
// Generate UUID
|
||
|
|
let uuid = generate_uuid();
|
||
|
|
|
||
|
|
// Format duration
|
||
|
|
let formatted = format_duration(125); // "2:05"
|
||
|
|
|
||
|
|
// Format file size
|
||
|
|
let size = format_file_size(1024); // "1.00 KB"
|
||
|
|
```
|
||
|
|
|
||
|
|
## Exemples d'Usage Complets
|
||
|
|
|
||
|
|
### Exemple 1: Créer et valider un utilisateur
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::types::User;
|
||
|
|
use veza_common::utils::validation::validate_email_result;
|
||
|
|
|
||
|
|
fn create_user(username: String, email: String) -> Result<User, String> {
|
||
|
|
// Validate email
|
||
|
|
validate_email_result(&email)?;
|
||
|
|
|
||
|
|
// Create user
|
||
|
|
let user = User::new(1, username, email);
|
||
|
|
|
||
|
|
// Validate user data
|
||
|
|
user.validate()?;
|
||
|
|
|
||
|
|
Ok(user)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Exemple 2: Configuration de base de données
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::config::DatabaseConfig;
|
||
|
|
use veza_common::error::CommonResult;
|
||
|
|
|
||
|
|
fn setup_database() -> CommonResult<DatabaseConfig> {
|
||
|
|
let config = DatabaseConfig::new(
|
||
|
|
std::env::var("DATABASE_URL")
|
||
|
|
.unwrap_or_else(|_| "postgresql://localhost/mydb".to_string()),
|
||
|
|
20
|
||
|
|
);
|
||
|
|
|
||
|
|
// Validate configuration
|
||
|
|
config.validate()?;
|
||
|
|
|
||
|
|
Ok(config)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Exemple 3: API Response avec pagination
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::types::{ApiResponse, PaginatedResponse};
|
||
|
|
|
||
|
|
fn get_users(page: u32, limit: u32) -> ApiResponse<PaginatedResponse<User>> {
|
||
|
|
let items = vec![/* ... */];
|
||
|
|
let total = 100;
|
||
|
|
|
||
|
|
let paginated = PaginatedResponse::new(items, total, page, limit);
|
||
|
|
ApiResponse::success(paginated)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Exemple 4: Gestion d'erreurs
|
||
|
|
|
||
|
|
```rust
|
||
|
|
use veza_common::error::{CommonError, CommonResult};
|
||
|
|
|
||
|
|
fn process_request(data: &str) -> CommonResult<String> {
|
||
|
|
if data.is_empty() {
|
||
|
|
return Err(CommonError::ValidationError(
|
||
|
|
"Data cannot be empty".to_string()
|
||
|
|
));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Process data...
|
||
|
|
Ok("processed".to_string())
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Tests
|
||
|
|
|
||
|
|
La bibliothèque inclut une infrastructure de tests complète dans `tests/common_tests.rs` avec:
|
||
|
|
|
||
|
|
- **Fixtures**: Fonctions pour créer des données de test
|
||
|
|
- **Helpers**: Fonctions utilitaires pour les tests
|
||
|
|
- **Exemples**: Tests d'intégration complets
|
||
|
|
|
||
|
|
Exécuter les tests:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cargo test
|
||
|
|
```
|
||
|
|
|
||
|
|
## Documentation API
|
||
|
|
|
||
|
|
Pour générer la documentation complète:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cargo doc --open
|
||
|
|
```
|
||
|
|
|
||
|
|
## Structure du Projet
|
||
|
|
|
||
|
|
```
|
||
|
|
veza-common/
|
||
|
|
├── src/
|
||
|
|
│ ├── lib.rs # Module principal
|
||
|
|
│ ├── types/ # Types partagés (User, Track, Playlist)
|
||
|
|
│ ├── error.rs # Gestion d'erreurs
|
||
|
|
│ ├── config/ # Configurations (Database, Redis)
|
||
|
|
│ └── utils/ # Utilitaires
|
||
|
|
│ ├── validation.rs
|
||
|
|
│ ├── serialization.rs
|
||
|
|
│ ├── date.rs
|
||
|
|
│ └── logging.rs
|
||
|
|
├── tests/
|
||
|
|
│ └── common_tests.rs # Tests d'intégration
|
||
|
|
└── Cargo.toml
|
||
|
|
```
|
||
|
|
|
||
|
|
## Licence
|
||
|
|
|
||
|
|
MIT
|
||
|
|
|
||
|
|
## Contribution
|
||
|
|
|
||
|
|
Les contributions sont les bienvenues ! Veuillez suivre les standards de code définis dans `ORIGIN_CODE_STANDARDS.md`.
|
||
|
|
|