1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum ResourceType {
9 CPU,
11 GPU,
13 Memory,
15 Storage,
17 Network,
19 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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
38pub enum CapabilityLevel {
39 VeryLow,
41 Low,
43 Medium,
45 High,
47 VeryHigh,
49 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 pub fn to_numeric(&self) -> f64 {
95 (*self).into()
96 }
97
98 pub fn from_numeric(score: f64) -> Self {
100 score.into()
101 }
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct ResourceRequirement {
107 pub resource_type: ResourceType,
109 pub minimum: ResourceAmount,
111 pub recommended: Option<ResourceAmount>,
113 pub maximum: Option<ResourceAmount>,
115 pub preferred_vendor: Option<String>,
117 pub constraints: Vec<ResourceConstraint>,
119 pub is_critical: bool,
121 pub weight: f64,
123}
124
125impl ResourceRequirement {
126 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 pub fn minimum_gb(mut self, gb: f64) -> Self {
142 self.minimum = ResourceAmount::Gigabytes(gb);
143 self
144 }
145
146 pub fn recommended_gb(mut self, gb: f64) -> Self {
148 self.recommended = Some(ResourceAmount::Gigabytes(gb));
149 self
150 }
151
152 pub fn maximum_gb(mut self, gb: f64) -> Self {
154 self.maximum = Some(ResourceAmount::Gigabytes(gb));
155 self
156 }
157
158 pub fn minimum_level(mut self, level: CapabilityLevel) -> Self {
160 self.minimum = ResourceAmount::Level(level);
161 self
162 }
163
164 pub fn recommended_level(mut self, level: CapabilityLevel) -> Self {
166 self.recommended = Some(ResourceAmount::Level(level));
167 self
168 }
169
170 pub fn maximum_level(mut self, level: CapabilityLevel) -> Self {
172 self.maximum = Some(ResourceAmount::Level(level));
173 self
174 }
175
176 pub fn minimum_score(mut self, score: f64) -> Self {
178 self.minimum = ResourceAmount::Score(score);
179 self
180 }
181
182 pub fn recommended_score(mut self, score: f64) -> Self {
184 self.recommended = Some(ResourceAmount::Score(score));
185 self
186 }
187
188 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 pub fn add_constraint(mut self, constraint: ResourceConstraint) -> Self {
196 self.constraints.push(constraint);
197 self
198 }
199
200 pub fn critical(mut self) -> Self {
202 self.is_critical = true;
203 self
204 }
205
206 pub fn required(self) -> Self {
208 self
210 }
211
212 pub fn minimum_ghz(mut self, ghz: f64) -> Self {
214 self.minimum = ResourceAmount::Megahertz(ghz * 1000.0);
215 self
216 }
217
218 pub fn gpu_memory_gb(self, gb: f64) -> Self {
220 self.add_constraint(ResourceConstraint::RequiredFeature(format!("GPU Memory: {} GB", gb)))
222 }
223
224 pub fn storage_type(self, storage_type: String) -> Self {
226 self.add_constraint(ResourceConstraint::RequiredFeature(storage_type))
227 }
228
229 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 pub fn cores(mut self, cores: u32) -> Self {
237 self.minimum = ResourceAmount::Units(cores);
238 self
239 }
240
241 pub fn is_satisfied_by(&self, amount: &ResourceAmount) -> bool {
243 amount >= &self.minimum
244 }
245
246 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#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
267pub enum ResourceAmount {
268 Level(CapabilityLevel),
270 Gigabytes(f64),
272 Megahertz(f64),
274 Score(f64),
276 Units(u32),
278 Percentage(f64),
280 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 pub fn new_gb(gb: f64) -> Self {
301 ResourceAmount::Gigabytes(gb)
302 }
303
304 pub fn new_mb(mb: f64) -> Self {
306 ResourceAmount::Gigabytes(mb / 1024.0)
307 }
308
309 pub fn new_mhz(mhz: f64) -> Self {
311 ResourceAmount::Megahertz(mhz)
312 }
313
314 pub fn new_ghz(ghz: f64) -> Self {
316 ResourceAmount::Megahertz(ghz * 1000.0)
317 }
318
319 pub fn as_gb(&self) -> Option<f64> {
321 match self {
322 ResourceAmount::Gigabytes(gb) => Some(*gb),
323 _ => None,
324 }
325 }
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
330pub enum ResourceConstraint {
331 MinimumFrequency(f64),
333 MaxPowerConsumption(f64),
335 RequiredFeature(String),
337 VendorRestriction(Vec<String>),
339 ArchitectureRequirement(String),
341 MaxTemperature(f64),
343 Custom { name: String, value: String },
345}
346
347#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct ResourceGap {
350 pub resource_type: ResourceType,
352 pub required: ResourceAmount,
354 pub available: ResourceAmount,
356 pub severity: crate::types::RequirementSeverity,
358}
359
360#[derive(Debug, Clone, Serialize, Deserialize)]
362pub struct ResourcePool {
363 pub resources: std::collections::HashMap<ResourceType, ResourceAmount>,
365 pub utilization: std::collections::HashMap<ResourceType, f64>,
367 pub last_updated: chrono::DateTime<chrono::Utc>,
369}
370
371impl ResourcePool {
372 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 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 pub fn add_resource(&mut self, resource_type: ResourceType, amount: ResourceAmount) {
389 self.set_resource(resource_type, amount);
390 }
391
392 pub fn get_resource(&self, resource_type: &ResourceType) -> Option<&ResourceAmount> {
394 self.resources.get(resource_type)
395 }
396
397 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 pub fn get_utilization(&self, resource_type: &ResourceType) -> f64 {
405 self.utilization.get(resource_type).copied().unwrap_or(0.0)
406 }
407
408 pub fn has_resource(&self, resource_type: &ResourceType) -> bool {
410 self.resources.contains_key(resource_type)
411 }
412
413 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, }
422 } else {
423 0.0
424 }
425 }
426
427 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, }
436 } else {
437 false
438 }
439 }
440
441 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 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 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 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, }
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 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}