use std::{borrow::Cow, cmp::Ordering};
use memory::{RAMMemoryType, SSDMemoryType};
use serde::{Deserialize, Serialize};
use self::{
cpu::CPU,
gpu::GPU,
memory::Memory,
requirement::ResourceRequirement,
traits::{Fulfillable, Price},
};
use crate::hash::Hashable;
pub mod cpu;
pub mod gpu;
pub mod memory;
pub mod requirement;
pub mod traits;
#[derive(Debug, Serialize, Deserialize, Clone, Hash, PartialEq, Default)]
#[serde(rename_all = "camelCase")]
pub struct Resource {
pub ram: Memory<RAMMemoryType>,
pub ssd: Memory<SSDMemoryType>,
pub gpus: Vec<GPU>,
pub cpu: CPU,
}
impl PartialOrd for Resource {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Eq for Resource {}
impl Ord for Resource {
fn cmp(&self, other: &Self) -> Ordering {
let my_best_gpu = self.gpus.iter().max();
let other_best_gpu = other.gpus.iter().max();
let gpu_ordering = if let Some(my_best_gpu) = my_best_gpu {
if let Some(other_best_gpu) = other_best_gpu {
my_best_gpu.cmp(other_best_gpu)
} else {
Ordering::Greater
}
} else if other_best_gpu.is_some() {
Ordering::Less
} else {
Ordering::Equal
};
gpu_ordering
.then(self.ram.cmp(&other.ram))
.then(self.cpu.cmp(&other.cpu))
}
}
impl Price for Resource {
fn price(&self) -> f64 {
100.0
}
}
impl Hashable for Resource {
fn collect(&self) -> Cow<[u8]> {
serde_json::to_vec(self).unwrap().into()
}
}
impl Fulfillable<ResourceRequirement> for Resource {
fn fulfills(&self, req: &ResourceRequirement) -> bool {
if let Some(min_ram) = req.min_ram {
if self.ram.size < min_ram {
return false;
}
}
if let Some(min_ssd) = req.min_ssd {
if self.ssd.size < min_ssd {
return false;
}
}
if let Some(min_cpu_cores) = req.min_cpu_cores {
if self.cpu.specs().cores < min_cpu_cores {
return false;
}
}
if let Some(min_vram) = req.min_vram {
for gpu in self.gpus.iter() {
if gpu.specs().memory.size < min_vram {
return false;
}
}
}
let mut fulfilled_gpu_is: Vec<usize> = vec![];
for gpu_req in req.min_gpu.iter() {
for (i, gpu) in self.gpus.iter().enumerate() {
if fulfilled_gpu_is.contains(&i) {
continue;
}
if gpu.fulfills(gpu_req) {
fulfilled_gpu_is.push(i);
break;
}
}
}
if fulfilled_gpu_is.len() != req.min_gpu.len() {
return false;
}
true
}
}
#[cfg(test)]
mod tests {
use memory::GIGA_BYTE;
use super::*;
use crate::resource::{
cpu::CPUSpecs,
gpu::{GPUMemoryType, GPUSpecs},
memory::RAMMemoryType,
};
#[test]
fn test_serialization() {
let rs = vec![
Resource {
ram: Memory {
size: 16 * GIGA_BYTE,
r#type: RAMMemoryType::DDR4,
},
ssd: Memory {
size: 16 * GIGA_BYTE,
r#type: SSDMemoryType::NVMeGen3,
},
gpus: vec![GPU::Specs(GPUSpecs {
cores: 3_584,
memory: Memory {
size: 8 * GIGA_BYTE,
r#type: GPUMemoryType::GDDR6,
},
clock_rate: 1_320_000_000,
})],
cpu: CPU::Specs(CPUSpecs {
cores: 16,
clock_rate: 3_800_000_000,
}),
},
Resource {
ram: Memory {
size: 16 * GIGA_BYTE,
r#type: RAMMemoryType::DDR4,
},
ssd: Memory {
size: 16 * GIGA_BYTE,
r#type: SSDMemoryType::NVMeGen3,
},
gpus: vec![GPU::Model(gpu::GPUModel::GeForceRtx3060_12GB)],
cpu: CPU::Model(cpu::CPUModel::Ryzen7),
},
];
let s = serde_json::to_string_pretty(&rs).unwrap();
println!("{}", s);
let rs: Vec<Resource> = serde_json::from_str(&s).unwrap();
println!("{:?}", rs);
}
}