system_analysis/
resources.rs

1//! Resource management and requirement modeling.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// Types of system resources
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum ResourceType {
9    /// CPU processing power
10    CPU,
11    /// GPU processing power
12    GPU,
13    /// System memory (RAM)
14    Memory,
15    /// Storage (disk space and I/O)
16    Storage,
17    /// Network bandwidth and latency
18    Network,
19    /// Custom resource type
20    Custom(u32),
21}
22
23impl fmt::Display for ResourceType {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            ResourceType::CPU => write!(f, "CPU"),
27            ResourceType::GPU => write!(f, "GPU"),
28            ResourceType::Memory => write!(f, "Memory"),
29            ResourceType::Storage => write!(f, "Storage"),
30            ResourceType::Network => write!(f, "Network"),
31            ResourceType::Custom(id) => write!(f, "Custom({})", id),
32        }
33    }
34}
35
36/// Capability levels for resources
37#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
38pub enum CapabilityLevel {
39    /// Very low capability
40    VeryLow,
41    /// Low capability
42    Low,
43    /// Medium capability
44    Medium,
45    /// High capability
46    High,
47    /// Very high capability
48    VeryHigh,
49    /// Exceptional capability
50    Exceptional,
51}
52
53impl fmt::Display for CapabilityLevel {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        match self {
56            CapabilityLevel::VeryLow => write!(f, "Very Low"),
57            CapabilityLevel::Low => write!(f, "Low"),
58            CapabilityLevel::Medium => write!(f, "Medium"),
59            CapabilityLevel::High => write!(f, "High"),
60            CapabilityLevel::VeryHigh => write!(f, "Very High"),
61            CapabilityLevel::Exceptional => write!(f, "Exceptional"),
62        }
63    }
64}
65
66impl From<f64> for CapabilityLevel {
67    fn from(score: f64) -> Self {
68        match score {
69            score if score >= 9.0 => CapabilityLevel::Exceptional,
70            score if score >= 7.5 => CapabilityLevel::VeryHigh,
71            score if score >= 6.0 => CapabilityLevel::High,
72            score if score >= 4.0 => CapabilityLevel::Medium,
73            score if score >= 2.0 => CapabilityLevel::Low,
74            _ => CapabilityLevel::VeryLow,
75        }
76    }
77}
78
79impl From<CapabilityLevel> for f64 {
80    fn from(level: CapabilityLevel) -> Self {
81        match level {
82            CapabilityLevel::Exceptional => 10.0,
83            CapabilityLevel::VeryHigh => 9.0,
84            CapabilityLevel::High => 7.0,
85            CapabilityLevel::Medium => 5.0,
86            CapabilityLevel::Low => 3.0,
87            CapabilityLevel::VeryLow => 1.0,
88        }
89    }
90}
91
92impl CapabilityLevel {
93    /// Convert to numeric score
94    pub fn to_numeric(&self) -> f64 {
95        (*self).into()
96    }
97    
98    /// Create from numeric score
99    pub fn from_numeric(score: f64) -> Self {
100        score.into()
101    }
102}
103
104/// Resource requirement specification
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct ResourceRequirement {
107    /// Type of resource
108    pub resource_type: ResourceType,
109    /// Minimum required level/amount
110    pub minimum: ResourceAmount,
111    /// Recommended level/amount
112    pub recommended: Option<ResourceAmount>,
113    /// Maximum useful level/amount
114    pub maximum: Option<ResourceAmount>,
115    /// Preferred vendor/technology
116    pub preferred_vendor: Option<String>,
117    /// Additional constraints
118    pub constraints: Vec<ResourceConstraint>,
119    /// Whether this requirement is critical
120    pub is_critical: bool,
121    /// Importance weight (0.0 to 1.0)
122    pub weight: f64,
123}
124
125impl ResourceRequirement {
126    /// Create a new resource requirement
127    pub fn new(resource_type: ResourceType) -> Self {
128        Self {
129            resource_type,
130            minimum: ResourceAmount::Level(CapabilityLevel::Medium),
131            recommended: None,
132            maximum: None,
133            preferred_vendor: None,
134            constraints: Vec::new(),
135            is_critical: true,
136            weight: 1.0,
137        }
138    }
139
140    /// Set minimum amount in GB (for memory/storage)
141    pub fn minimum_gb(mut self, gb: f64) -> Self {
142        self.minimum = ResourceAmount::Gigabytes(gb);
143        self
144    }
145
146    /// Set recommended amount in GB (for memory/storage)
147    pub fn recommended_gb(mut self, gb: f64) -> Self {
148        self.recommended = Some(ResourceAmount::Gigabytes(gb));
149        self
150    }
151
152    /// Set maximum amount in GB (for memory/storage)
153    pub fn maximum_gb(mut self, gb: f64) -> Self {
154        self.maximum = Some(ResourceAmount::Gigabytes(gb));
155        self
156    }
157
158    /// Set minimum capability level
159    pub fn minimum_level(mut self, level: CapabilityLevel) -> Self {
160        self.minimum = ResourceAmount::Level(level);
161        self
162    }
163
164    /// Set recommended capability level
165    pub fn recommended_level(mut self, level: CapabilityLevel) -> Self {
166        self.recommended = Some(ResourceAmount::Level(level));
167        self
168    }
169
170    /// Set maximum capability level
171    pub fn maximum_level(mut self, level: CapabilityLevel) -> Self {
172        self.maximum = Some(ResourceAmount::Level(level));
173        self
174    }
175
176    /// Set minimum score (0-10)
177    pub fn minimum_score(mut self, score: f64) -> Self {
178        self.minimum = ResourceAmount::Score(score);
179        self
180    }
181
182    /// Set recommended score (0-10)
183    pub fn recommended_score(mut self, score: f64) -> Self {
184        self.recommended = Some(ResourceAmount::Score(score));
185        self
186    }
187
188    /// Set preferred vendor
189    pub fn preferred_vendor(mut self, vendor: Option<impl Into<String>>) -> Self {
190        self.preferred_vendor = vendor.map(|v| v.into());
191        self
192    }
193
194    /// Add a constraint
195    pub fn add_constraint(mut self, constraint: ResourceConstraint) -> Self {
196        self.constraints.push(constraint);
197        self
198    }
199
200    /// Mark as critical requirement
201    pub fn critical(mut self) -> Self {
202        self.is_critical = true;
203        self
204    }
205
206    /// Mark as required
207    pub fn required(self) -> Self {
208        // Already critical by default if not set
209        self
210    }
211    
212    /// Set minimum GHz (for CPU)
213    pub fn minimum_ghz(mut self, ghz: f64) -> Self {
214        self.minimum = ResourceAmount::Megahertz(ghz * 1000.0);
215        self
216    }
217    
218    /// Set GPU memory requirement
219    pub fn gpu_memory_gb(self, gb: f64) -> Self {
220        // For GPU memory we can use a custom constraint
221        self.add_constraint(ResourceConstraint::RequiredFeature(format!("GPU Memory: {} GB", gb)))
222    }
223    
224    /// Set storage type
225    pub fn storage_type(self, storage_type: String) -> Self {
226        self.add_constraint(ResourceConstraint::RequiredFeature(storage_type))
227    }
228    
229    /// Set minimum Mbps (for network)
230    pub fn minimum_mbps(mut self, mbps: f64) -> Self {
231        self.minimum = ResourceAmount::Custom { value: mbps, unit: "Mbps".to_string() };
232        self
233    }
234    
235    /// Set number of cores
236    pub fn cores(mut self, cores: u32) -> Self {
237        self.minimum = ResourceAmount::Units(cores);
238        self
239    }
240
241    /// Check if a resource amount meets this requirement
242    pub fn is_satisfied_by(&self, amount: &ResourceAmount) -> bool {
243        amount >= &self.minimum
244    }
245
246    /// Get the gap between available and required resources
247    pub fn get_gap(&self, available: &ResourceAmount) -> Option<ResourceGap> {
248        if self.is_satisfied_by(available) {
249            None
250        } else {
251            Some(ResourceGap {
252                resource_type: self.resource_type,
253                required: self.minimum.clone(),
254                available: available.clone(),
255                severity: if self.is_critical {
256                    crate::types::RequirementSeverity::Critical
257                } else {
258                    crate::types::RequirementSeverity::High
259                },
260            })
261        }
262    }
263}
264
265/// Different ways to specify resource amounts
266#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
267pub enum ResourceAmount {
268    /// Capability level
269    Level(CapabilityLevel),
270    /// Amount in gigabytes
271    Gigabytes(f64),
272    /// Amount in megahertz
273    Megahertz(f64),
274    /// Score from 0 to 10
275    Score(f64),
276    /// Number of units
277    Units(u32),
278    /// Percentage (0.0 to 100.0)
279    Percentage(f64),
280    /// Custom amount with label
281    Custom { value: f64, unit: String },
282}
283
284impl fmt::Display for ResourceAmount {
285    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286        match self {
287            ResourceAmount::Level(level) => write!(f, "{}", level),
288            ResourceAmount::Gigabytes(gb) => write!(f, "{:.1} GB", gb),
289            ResourceAmount::Megahertz(mhz) => write!(f, "{:.0} MHz", mhz),
290            ResourceAmount::Score(score) => write!(f, "{:.1}/10", score),
291            ResourceAmount::Units(units) => write!(f, "{} units", units),
292            ResourceAmount::Percentage(pct) => write!(f, "{:.1}%", pct),
293            ResourceAmount::Custom { value, unit } => write!(f, "{:.1} {}", value, unit),
294        }
295    }
296}
297
298impl ResourceAmount {
299    /// Create a gigabyte amount
300    pub fn new_gb(gb: f64) -> Self {
301        ResourceAmount::Gigabytes(gb)
302    }
303    
304    /// Create a megabyte amount (converted to GB)
305    pub fn new_mb(mb: f64) -> Self {
306        ResourceAmount::Gigabytes(mb / 1024.0)
307    }
308    
309    /// Create a megahertz amount
310    pub fn new_mhz(mhz: f64) -> Self {
311        ResourceAmount::Megahertz(mhz)
312    }
313    
314    /// Create a gigahertz amount (converted to MHz)
315    pub fn new_ghz(ghz: f64) -> Self {
316        ResourceAmount::Megahertz(ghz * 1000.0)
317    }
318    
319    /// Get value as gigabytes if possible
320    pub fn as_gb(&self) -> Option<f64> {
321        match self {
322            ResourceAmount::Gigabytes(gb) => Some(*gb),
323            _ => None,
324        }
325    }
326}
327
328/// Resource constraints
329#[derive(Debug, Clone, Serialize, Deserialize)]
330pub enum ResourceConstraint {
331    /// Minimum frequency requirement
332    MinimumFrequency(f64),
333    /// Maximum power consumption in watts
334    MaxPowerConsumption(f64),
335    /// Required feature support
336    RequiredFeature(String),
337    /// Vendor restriction
338    VendorRestriction(Vec<String>),
339    /// Architecture requirement
340    ArchitectureRequirement(String),
341    /// Temperature constraint
342    MaxTemperature(f64),
343    /// Custom constraint
344    Custom { name: String, value: String },
345}
346
347/// Resource gap information
348#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct ResourceGap {
350    /// Resource type with gap
351    pub resource_type: ResourceType,
352    /// Required amount
353    pub required: ResourceAmount,
354    /// Available amount
355    pub available: ResourceAmount,
356    /// Severity of the gap
357    pub severity: crate::types::RequirementSeverity,
358}
359
360/// Resource pool for tracking available resources
361#[derive(Debug, Clone, Serialize, Deserialize)]
362pub struct ResourcePool {
363    /// Available resources by type
364    pub resources: std::collections::HashMap<ResourceType, ResourceAmount>,
365    /// Resource utilization tracking
366    pub utilization: std::collections::HashMap<ResourceType, f64>,
367    /// Last updated timestamp
368    pub last_updated: chrono::DateTime<chrono::Utc>,
369}
370
371impl ResourcePool {
372    /// Create a new empty resource pool
373    pub fn new() -> Self {
374        Self {
375            resources: std::collections::HashMap::new(),
376            utilization: std::collections::HashMap::new(),
377            last_updated: chrono::Utc::now(),
378        }
379    }
380
381    /// Add or update a resource
382    pub fn set_resource(&mut self, resource_type: ResourceType, amount: ResourceAmount) {
383        self.resources.insert(resource_type, amount);
384        self.last_updated = chrono::Utc::now();
385    }
386
387    /// Add a resource (alias for set_resource)
388    pub fn add_resource(&mut self, resource_type: ResourceType, amount: ResourceAmount) {
389        self.set_resource(resource_type, amount);
390    }
391    
392    /// Get available amount for a resource type
393    pub fn get_resource(&self, resource_type: &ResourceType) -> Option<&ResourceAmount> {
394        self.resources.get(resource_type)
395    }
396
397    /// Set resource utilization
398    pub fn set_utilization(&mut self, resource_type: ResourceType, utilization: f64) {
399        self.utilization.insert(resource_type, utilization.clamp(0.0, 100.0));
400        self.last_updated = chrono::Utc::now();
401    }
402
403    /// Get resource utilization
404    pub fn get_utilization(&self, resource_type: &ResourceType) -> f64 {
405        self.utilization.get(resource_type).copied().unwrap_or(0.0)
406    }
407
408    /// Check if a resource type is available
409    pub fn has_resource(&self, resource_type: &ResourceType) -> bool {
410        self.resources.contains_key(resource_type)
411    }
412    
413    /// Calculate utilization for a specific resource
414    pub fn calculate_utilization(&self, resource_type: &ResourceType, used: &ResourceAmount) -> f64 {
415        if let Some(available) = self.get_resource(resource_type) {
416            match (used, available) {
417                (ResourceAmount::Gigabytes(used_gb), ResourceAmount::Gigabytes(avail_gb)) => {
418                    (used_gb / avail_gb * 100.0).min(100.0)
419                }
420                _ => 0.0, // Default for unsupported combinations
421            }
422        } else {
423            0.0
424        }
425    }
426    
427    /// Check if a specific amount is available
428    pub fn is_available(&self, resource_type: &ResourceType, required: &ResourceAmount) -> bool {
429        if let Some(available) = self.get_resource(resource_type) {
430            match (required, available) {
431                (ResourceAmount::Gigabytes(req_gb), ResourceAmount::Gigabytes(avail_gb)) => {
432                    avail_gb >= req_gb
433                }
434                _ => false, // Conservative approach for unsupported combinations
435            }
436        } else {
437            false
438        }
439    }
440
441    /// Check if requirements can be satisfied
442    pub fn can_satisfy(&self, requirements: &[ResourceRequirement]) -> Vec<ResourceGap> {
443        let mut gaps = Vec::new();
444
445        for req in requirements {
446            if let Some(available) = self.get_resource(&req.resource_type) {
447                if let Some(gap) = req.get_gap(available) {
448                    gaps.push(gap);
449                }
450            } else {
451                // Resource not available at all
452                gaps.push(ResourceGap {
453                    resource_type: req.resource_type,
454                    required: req.minimum.clone(),
455                    available: ResourceAmount::Score(0.0),
456                    severity: if req.is_critical {
457                        crate::types::RequirementSeverity::Critical
458                    } else {
459                        crate::types::RequirementSeverity::High
460                    },
461                });
462            }
463        }
464
465        gaps
466    }
467
468    /// Calculate overall satisfaction score (0-10)
469    pub fn satisfaction_score(&self, requirements: &[ResourceRequirement]) -> f64 {
470        if requirements.is_empty() {
471            return 10.0;
472        }
473
474        let mut total_weight = 0.0;
475        let mut weighted_satisfaction = 0.0;
476
477        for req in requirements {
478            total_weight += req.weight;
479
480            let satisfaction = if let Some(available) = self.get_resource(&req.resource_type) {
481                self.calculate_satisfaction_ratio(&req.minimum, available)
482            } else {
483                0.0
484            };
485
486            weighted_satisfaction += satisfaction * req.weight;
487        }
488
489        if total_weight > 0.0 {
490            (weighted_satisfaction / total_weight) * 10.0
491        } else {
492            10.0
493        }
494    }
495
496    /// Calculate satisfaction ratio for a requirement
497    fn calculate_satisfaction_ratio(&self, required: &ResourceAmount, available: &ResourceAmount) -> f64 {
498        match (required, available) {
499            (ResourceAmount::Score(req), ResourceAmount::Score(avail)) => (avail / req).min(1.0),
500            (ResourceAmount::Gigabytes(req), ResourceAmount::Gigabytes(avail)) => (avail / req).min(1.0),
501            (ResourceAmount::Level(req), ResourceAmount::Level(avail)) => {
502                let req_score: f64 = (*req).into();
503                let avail_score: f64 = (*avail).into();
504                (avail_score / req_score).min(1.0)
505            }
506            (ResourceAmount::Units(req), ResourceAmount::Units(avail)) => {
507                (*avail as f64 / *req as f64).min(1.0)
508            }
509            (ResourceAmount::Percentage(req), ResourceAmount::Percentage(avail)) => {
510                (avail / req).min(1.0)
511            }
512            _ => 0.5, // Different types, assume partial satisfaction
513        }
514    }
515}
516
517impl Default for ResourcePool {
518    fn default() -> Self {
519        Self::new()
520    }
521}
522#[cfg(test)]
523mod tests {
524    use super::*;
525
526    #[test]
527    fn test_capability_level_conversion() {
528        assert_eq!(CapabilityLevel::VeryLow.to_numeric(), 1.0);
529        assert_eq!(CapabilityLevel::Low.to_numeric(), 3.0);
530        assert_eq!(CapabilityLevel::Medium.to_numeric(), 5.0);
531        assert_eq!(CapabilityLevel::High.to_numeric(), 7.0);
532        assert_eq!(CapabilityLevel::VeryHigh.to_numeric(), 9.0);
533        assert_eq!(CapabilityLevel::Exceptional.to_numeric(), 10.0);
534
535        assert_eq!(CapabilityLevel::from_numeric(1.0), CapabilityLevel::VeryLow);
536        assert_eq!(CapabilityLevel::from_numeric(5.0), CapabilityLevel::Medium);
537        assert_eq!(CapabilityLevel::from_numeric(10.0), CapabilityLevel::Exceptional);
538        assert_eq!(CapabilityLevel::from_numeric(6.5), CapabilityLevel::High);
539    }
540
541    #[test]
542    fn test_resource_amount_creation() {
543        let amount_gb = ResourceAmount::new_gb(16.0);
544        assert_eq!(amount_gb.as_gb(), Some(16.0));
545
546        let amount_mb = ResourceAmount::new_mb(1024.0);
547        assert_eq!(amount_mb.as_gb(), Some(1.0));
548
549        let amount_mhz = ResourceAmount::new_mhz(3200.0);
550        let amount_ghz = ResourceAmount::new_ghz(2.5);
551
552        // Basic test that they were created correctly
553        assert!(matches!(amount_mhz, ResourceAmount::Megahertz(_)));
554        assert!(matches!(amount_ghz, ResourceAmount::Megahertz(_)));
555    }
556
557    #[test]
558    fn test_resource_requirement_basic() {
559        let requirement = ResourceRequirement::new(ResourceType::Memory)
560            .minimum_gb(8.0)
561            .recommended_gb(16.0);
562
563        assert_eq!(requirement.resource_type, ResourceType::Memory);
564        assert_eq!(requirement.minimum, ResourceAmount::Gigabytes(8.0));
565        assert!(requirement.recommended.is_some());
566        assert!(requirement.is_critical);
567    }
568
569    #[test]
570    fn test_resource_pool_basic() {
571        let mut pool = ResourcePool::new();
572        
573        let memory_resource = ResourceAmount::new_gb(32.0);
574        pool.add_resource(ResourceType::Memory, memory_resource);
575
576        assert!(pool.has_resource(&ResourceType::Memory));
577        assert!(!pool.has_resource(&ResourceType::GPU));
578
579        let retrieved = pool.get_resource(&ResourceType::Memory);
580        assert_eq!(retrieved.unwrap().as_gb(), Some(32.0));
581    }
582}