Coverage Report

Created: 2025-10-06 12:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/jgrusewski/Work/foxhunt/config/src/schemas.rs
Line
Count
Source
1
//! Configuration schemas and cloud storage configurations.
2
//!
3
//! This module defines configuration schemas for various cloud storage backends
4
//! and configuration versioning. Primarily focused on S3-compatible storage
5
//! for model artifacts and configuration management in the Foxhunt trading system.
6
7
use chrono::{DateTime, Utc};
8
use serde::{Deserialize, Serialize};
9
use std::collections::HashMap;
10
use std::time::Duration;
11
use uuid::Uuid;
12
13
/// Configuration schema metadata for versioning and tracking.
14
///
15
/// Provides versioning and audit trail information for configuration schemas.
16
/// Used to track configuration changes over time and maintain compatibility
17
/// across different versions of the trading system.
18
#[derive(Debug, Clone, Serialize, Deserialize)]
19
pub struct ConfigSchema {
20
    /// Unique identifier for this configuration schema
21
    pub id: Uuid,
22
    /// Semantic version string (e.g., "1.2.3")
23
    pub version: String,
24
    /// Timestamp when this schema was created
25
    pub created_at: DateTime<Utc>,
26
    /// Timestamp when this schema was last updated
27
    pub updated_at: DateTime<Utc>,
28
}
29
30
/// Amazon S3 and S3-compatible storage configuration.
31
///
32
/// Configures access to S3 or S3-compatible storage services for storing
33
/// ML model artifacts, configuration backups, and other binary data.
34
/// Supports various authentication methods and connection options.
35
#[derive(Debug, Clone, Serialize, Deserialize)]
36
pub struct S3Config {
37
    /// S3 bucket name for storing model artifacts and data
38
    pub bucket_name: String,
39
    /// AWS region or S3-compatible service region
40
    pub region: String,
41
    /// AWS access key ID (optional, can use IAM roles or environment variables)
42
    pub access_key_id: Option<String>,
43
    /// AWS secret access key (optional, can use IAM roles or environment variables)
44
    pub secret_access_key: Option<String>,
45
    /// AWS session token for temporary credentials (optional)
46
    pub session_token: Option<String>,
47
    /// Custom S3-compatible endpoint URL (e.g., MinIO, DigitalOcean Spaces)
48
    pub endpoint_url: Option<String>,
49
    /// Force path-style URLs instead of virtual-hosted-style URLs
50
    pub force_path_style: bool,
51
    /// Request timeout duration for S3 operations
52
    pub timeout: Duration,
53
    /// Maximum number of retry attempts for failed requests
54
    pub max_retry_attempts: u32,
55
    /// Enable SSL/TLS for S3 connections
56
    pub use_ssl: bool,
57
}
58
59
impl S3Config {
60
    /// Validates the S3 configuration for correctness.
61
    ///
62
    /// Performs validation checks on the S3 configuration to ensure all
63
    /// required fields are present and have valid values before attempting
64
    /// to establish connections to S3 services.
65
    ///
66
    /// # Errors
67
    ///
68
    /// Returns an error string if the configuration is invalid:
69
    /// - Empty bucket name
70
    /// - Empty region
71
    /// - Invalid endpoint URL format
72
0
    pub fn validate(&self) -> Result<(), String> {
73
0
        if self.bucket_name.is_empty() {
74
0
            return Err("S3 bucket name cannot be empty".to_string());
75
0
        }
76
0
        if self.region.is_empty() {
77
0
            return Err("S3 region cannot be empty".to_string());
78
0
        }
79
0
        Ok(())
80
0
    }
81
}
82
83
/// Asset classification configuration for sector and type categorization.
84
///
85
/// Provides configuration-driven asset classification that replaces hardcoded
86
/// symbol-based classification logic. Supports flexible categorization rules
87
/// based on instrument properties rather than specific symbol names.
88
#[derive(Debug, Clone, Serialize, Deserialize)]
89
pub struct AssetClassificationConfig {
90
    /// Classification rules based on asset type patterns
91
    pub asset_type_rules: HashMap<String, String>,
92
    /// Default classifications for different asset categories
93
    pub default_sectors: HashMap<String, String>,
94
    /// Regex patterns for currency pair detection
95
    pub currency_patterns: Vec<String>,
96
    /// Regex patterns for cryptocurrency detection
97
    pub crypto_patterns: Vec<String>,
98
}
99
100
impl AssetClassificationConfig {
101
    /// Creates a new asset classification configuration with default rules.
102
0
    pub fn new() -> Self {
103
0
        let mut asset_type_rules = HashMap::new();
104
0
        asset_type_rules.insert("EQUITY".to_string(), "Equity".to_string());
105
0
        asset_type_rules.insert("FOREX".to_string(), "Currencies".to_string());
106
0
        asset_type_rules.insert("CRYPTO".to_string(), "Cryptocurrency".to_string());
107
0
        asset_type_rules.insert("COMMODITY".to_string(), "Commodities".to_string());
108
0
        asset_type_rules.insert("BOND".to_string(), "Fixed Income".to_string());
109
110
0
        let mut default_sectors = HashMap::new();
111
0
        default_sectors.insert("Equity".to_string(), "Other".to_string());
112
0
        default_sectors.insert("Currencies".to_string(), "Currencies".to_string());
113
0
        default_sectors.insert("Cryptocurrency".to_string(), "Cryptocurrency".to_string());
114
0
        default_sectors.insert("Commodities".to_string(), "Commodities".to_string());
115
0
        default_sectors.insert("Fixed Income".to_string(), "Fixed Income".to_string());
116
117
0
        Self {
118
0
            asset_type_rules,
119
0
            default_sectors,
120
0
            currency_patterns: vec![
121
0
                r"^[A-Z]{3}[A-Z]{3}$".to_string(), // USDEUR format
122
0
                r".*USD.*".to_string(),
123
0
                r".*EUR.*".to_string(),
124
0
                r".*GBP.*".to_string(),
125
0
                r".*JPY.*".to_string(),
126
0
            ],
127
0
            crypto_patterns: vec![
128
0
                r".*BTC.*".to_string(),
129
0
                r".*ETH.*".to_string(),
130
0
                r".*CRYPTO.*".to_string(),
131
0
            ],
132
0
        }
133
0
    }
134
135
    /// Classifies an instrument based on configuration rules.
136
0
    pub fn classify_sector(&self, instrument_id: &str, asset_type: Option<&str>) -> String {
137
        // First try to classify based on asset type if provided
138
0
        if let Some(asset_type) = asset_type {
139
0
            if let Some(sector) = self.asset_type_rules.get(asset_type) {
140
0
                return sector.clone();
141
0
            }
142
0
        }
143
144
        // Check for currency patterns
145
0
        for pattern in &self.currency_patterns {
146
0
            if let Ok(regex) = regex::Regex::new(pattern) {
147
0
                if regex.is_match(instrument_id) {
148
0
                    return "Currencies".to_string();
149
0
                }
150
0
            }
151
        }
152
153
        // Check for crypto patterns
154
0
        for pattern in &self.crypto_patterns {
155
0
            if let Ok(regex) = regex::Regex::new(pattern) {
156
0
                if regex.is_match(instrument_id) {
157
0
                    return "Cryptocurrency".to_string();
158
0
                }
159
0
            }
160
        }
161
162
        // Default classification
163
0
        "Other".to_string()
164
0
    }
165
}
166
167
impl Default for AssetClassificationConfig {
168
0
    fn default() -> Self {
169
0
        Self::new()
170
0
    }
171
}
172
173
impl Default for S3Config {
174
0
    fn default() -> Self {
175
0
        Self {
176
0
            bucket_name: "foxhunt-models".to_string(),
177
0
            region: "us-east-1".to_string(),
178
0
            access_key_id: None,
179
0
            secret_access_key: None,
180
0
            session_token: None,
181
0
            endpoint_url: None,
182
0
            force_path_style: false,
183
0
            timeout: Duration::from_secs(30),
184
0
            max_retry_attempts: 3,
185
0
            use_ssl: true,
186
0
        }
187
0
    }
188
}