#![forbid(unsafe_code)]
#![allow(dead_code, unused_macros)]
use std::fmt::Display;
use sha3::{Digest, Sha3_512};
use sysinfo::{self, DiskExt, ProcessorExt, System, SystemExt};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IdentifierType {
CPU,
RAM,
DISK,
}
impl IdentifierType {
pub fn as_str(&self) -> &'static str {
match self {
IdentifierType::CPU => "CPU",
IdentifierType::RAM => "RAM",
IdentifierType::DISK => "DISK",
}
}
}
impl From<&str> for IdentifierType {
fn from(name: &str) -> Self {
match name {
"CPU" => IdentifierType::CPU,
"RAM" => IdentifierType::RAM,
"DISK" => IdentifierType::DISK,
_ => panic!("Unknown identifier type name: {}", name),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IdentifierTypeData {
pub key: String,
pub value: String,
}
impl IdentifierTypeData {
pub fn new(key: &str, value: &str) -> Self {
IdentifierTypeData {
key: key.to_string(),
value: value.to_string(),
}
}
pub fn key(&self) -> &str {
&self.key
}
pub fn value(&self) -> &str {
&self.value
}
}
impl Display for IdentifierTypeData {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}={}", self.key, self.value)
}
}
pub struct IdentifierTypeDataBuilder {
identifier: IdentifierType,
data: Vec<IdentifierTypeData>,
}
impl IdentifierTypeDataBuilder {
pub fn new(identifier: IdentifierType) -> Self {
IdentifierTypeDataBuilder {
identifier,
data: Vec::new(),
}
}
pub fn add<T: Into<String>>(&mut self, key: T, value: T) -> &mut Self {
self.data.push(IdentifierTypeData {
key: key.into(),
value: value.into(),
});
self
}
pub fn build(self) -> String {
let mut data = String::new();
data.push_str(self.identifier.as_str());
data.push('(');
for item in self.data {
data.push_str(&format!("{}={}, ", item.key, item.value));
}
data.pop();
data.pop();
data.push(')');
data
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IdentifierTypeDataList {
pub identifier: IdentifierType,
pub data: Vec<IdentifierTypeData>,
}
impl IdentifierTypeDataList {
pub fn new(identifier: IdentifierType) -> Self {
IdentifierTypeDataList {
identifier,
data: Vec::new(),
}
}
pub fn build(&self) -> String {
match self.identifier {
IdentifierType::CPU => self.build_cpu(),
IdentifierType::RAM => self.build_ram(),
IdentifierType::DISK => self.build_disk(),
}
}
fn build_cpu(&self) -> String {
let mut sys = System::new_all();
sys.refresh_all();
let cpu = sys.processors();
let brand = cpu[0].brand();
let vendor = cpu[0].vendor_id();
let frequency = cpu[0].frequency();
let cores = cpu.len();
let mut result = String::new();
let mut identifier_type = IdentifierTypeDataBuilder::new(IdentifierType::CPU);
identifier_type.add("b", brand.to_lowercase().trim());
identifier_type.add("v", vendor.to_lowercase().trim());
identifier_type.add("f", &frequency.to_string());
identifier_type.add("c", &cores.to_string());
result.push_str(&identifier_type.build());
result
}
fn build_ram(&self) -> String {
let mut sys = System::new_all();
sys.refresh_all();
let ram = sys.total_memory();
let mut result = String::new();
let mut identifier_type = IdentifierTypeDataBuilder::new(IdentifierType::RAM);
identifier_type.add("t", &ram.to_string());
result.push_str(&identifier_type.build());
result
}
fn build_disk(&self) -> String {
let mut sys = System::new_all();
sys.refresh_all();
let disks = sys.disks();
let mut result = String::new();
for disk in disks {
if disk.is_removable() {
continue;
}
let total_space = disk.total_space();
let mut identifier_type = IdentifierTypeDataBuilder::new(IdentifierType::DISK);
identifier_type.add("t", &total_space.to_string());
result.push_str(&identifier_type.build());
}
result
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct Identifier {
pub name: Option<String>,
pub data: Vec<IdentifierTypeDataList>,
}
impl Identifier {
pub fn new<T: Into<String>>(name: T) -> Self {
Identifier {
name: Some(name.into()),
data: Vec::new(),
}
}
pub fn to_string(&self, hash: bool) -> String {
let mut result = String::new();
if let Some(name) = &self.name {
result.push_str(name);
}
result.push('[');
for i in &self.data {
result.push_str(&i.build());
result.push_str(", ");
}
result.pop();
result.pop();
result.push(']');
if hash {
let mut hasher = Sha3_512::default();
let mut result_bytes = result.as_bytes();
Digest::update(&mut hasher, &mut result_bytes);
let result_hash = format!("{:x}", hasher.finalize());
return result_hash;
}
result
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct IdentifierBuilder {
pub name: Option<String>,
pub data: Vec<IdentifierTypeDataList>,
}
impl IdentifierBuilder {
pub fn new<T: Into<String>>(name: Option<T>, data: Vec<IdentifierTypeDataList>) -> Self {
if let Some(name) = name {
IdentifierBuilder {
name: Some(name.into()),
data,
}
} else {
IdentifierBuilder { name: None, data }
}
}
pub fn name<T: Into<String>>(&mut self, name: T) -> &mut Self {
self.name = Some(name.into());
self
}
pub fn add(&mut self, identifier: IdentifierType) -> &mut Self {
self.data.push(IdentifierTypeDataList::new(identifier));
self
}
pub fn build(self) -> Identifier {
Identifier {
name: self.name,
data: self.data,
}
}
}
mod tests {
#![allow(unused_imports)]
use super::*;
#[test]
fn test_identifier_builder() {
let mut builder = IdentifierBuilder::default();
builder.name("test");
builder.add(IdentifierType::CPU);
builder.add(IdentifierType::RAM);
builder.add(IdentifierType::DISK);
let identifier = builder.build();
assert_eq!(identifier.name, Some("test".to_string()));
assert_eq!(identifier.data.len(), 3);
println!("{}", identifier.to_string(false));
println!("{}", identifier.to_string(true));
}
}