use super::*;
use std::str::FromStr;
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default, serde::Deserialize)]
#[serde(rename_all = "kebab-case", default)]
pub struct Options {
pub allow: String,
pub capture_directory: String,
pub capture_on_error: StringBool,
pub capture_packets: StringBool,
pub capture_requests: StringBool,
pub capture_responses: StringBool,
pub capture_sockets: StringBool,
pub cause: String,
pub certificate_file: String,
pub checkpoint: StringInt,
pub child: StringBool,
pub client_subtype: String,
pub client_threads: StringInt,
pub client_type: String,
pub command_address: String,
pub command_allow_no_pass: String,
pub deny: String,
pub command_deny_no_pass: String,
pub command_enable: StringBool,
pub command_port: StringInt,
pub config_rotate: StringBool,
pub config_rotate_dir: String,
pub config_rotate_max: StringInt,
pub connection_timeout: StringInt,
pub core_priority: String,
pub cpu_species: String,
pub cpu_type: String,
pub cpu_usage: StringInt,
pub cpus: StringInt,
pub crl_file: String,
pub cuda_index: String,
pub cycle_rate: StringInt,
pub cycles: StringInt,
pub daemon: StringBool,
pub debug_sockets: StringBool,
pub disable_sleep_when_active: StringBool,
pub disable_viz: StringBool,
pub dump_after_deadline: StringBool,
pub exception_locations: StringBool,
pub exit_when_done: StringBool,
pub extra_core_args: String,
pub fold_anon: String,
pub gpu: String,
pub gpu_index: String,
pub gpu_usage: StringInt,
pub gui_enabled: String,
pub http_addresses: String,
pub https_addresses: String,
pub idle: StringBool,
pub log: String,
pub log_color: StringBool,
pub log_crlf: StringBool,
pub log_date: StringBool,
pub log_date_periodically: StringInt,
pub log_domain: StringBool,
pub log_domain_levels: String,
pub log_header: StringBool,
pub log_level: StringBool,
pub log_no_info_header: StringBool,
pub log_redirect: StringBool,
pub log_rotate: StringBool,
pub log_rotate_dir: String,
pub log_rotate_max: StringInt,
pub log_short_level: StringBool,
pub log_simple_domains: StringBool,
pub log_thread_id: StringBool,
pub log_thread_prefix: StringBool,
pub log_time: StringBool,
pub log_to_screen: StringBool,
pub log_truncate: StringBool,
pub machine_id: StringInt,
pub max_connect_time: StringInt,
pub max_connections: StringInt,
pub max_packet_size: String,
pub max_queue: StringInt,
pub max_request_length: StringInt,
pub max_shutdown_wait: StringInt,
pub max_slot_errors: StringInt,
pub max_unit_errors: StringInt,
pub max_units: StringInt,
pub memory: String,
pub min_connect_time: StringInt,
pub next_unit_percentage: StringInt,
pub priority: String,
pub no_assembly: StringBool,
pub open_web_control: StringBool,
pub opencl_index: String,
pub os_species: String,
pub os_type: String,
pub passkey: String,
pub password: String,
pub pause_on_battery: StringBool,
pub pause_on_start: StringBool,
pub paused: StringBool,
pub pid: StringBool,
pub pid_file: String,
pub power: Power,
pub private_key_file: String,
pub project_key: StringInt,
pub proxy: String,
pub proxy_enable: StringBool,
pub proxy_pass: String,
pub proxy_user: String,
pub respawn: StringBool,
pub service: StringBool,
pub service_description: String,
pub service_restart: StringBool,
pub service_restart_delay: StringInt,
pub session_cookie: String,
pub session_lifetime: StringInt,
pub session_timeout: StringInt,
pub smp: StringBool,
pub stack_traces: StringBool,
pub stall_detection_enabled: StringBool,
pub stall_percent: StringInt,
pub stall_timeout: StringInt,
pub team: StringInt,
pub user: String,
pub verbosity: StringInt,
pub web_allow: String,
pub web_deny: String,
pub web_enable: StringBool,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct StringBool(pub bool);
impl From<bool> for StringBool {
fn from(b: bool) -> Self {
Self(b)
}
}
impl std::fmt::Display for StringBool {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl<'de> serde::de::Deserialize<'de> for StringBool {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
match serde_json::from_str(serde::de::Deserialize::deserialize(deserializer)?) {
Ok(result) => Ok(Self(result)),
Err(e) => Err(serde::de::Error::custom(e.to_string())),
}
}
}
impl core::str::FromStr for StringBool {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(Self(str::parse(s)?))
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct StringInt(pub i64);
impl From<i64> for StringInt {
fn from(n: i64) -> Self {
Self(n)
}
}
impl std::fmt::Display for StringInt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl<'de> serde::de::Deserialize<'de> for StringInt {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
match serde_json::from_str(serde::de::Deserialize::deserialize(deserializer)?) {
Ok(n) => Ok(Self(n)),
Err(e) => Err(serde::de::Error::custom(e.to_string())),
}
}
}
impl core::str::FromStr for StringInt {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(Self(str::parse(s)?))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Power {
PowerNull,
PowerLight,
PowerMedium,
PowerFull,
}
impl std::default::Default for Power {
fn default() -> Self {
Power::PowerNull
}
}
impl core::str::FromStr for Power {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Ok(match s.to_uppercase().as_str() {
"" => Power::PowerNull,
"LIGHT" => Power::PowerLight,
"MEDIUM" => Power::PowerMedium,
"FULL" => Power::PowerFull,
_ => return Err(Error::msg(format!("s is invalid: {}", s))),
})
}
}
impl std::fmt::Display for Power {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Power::PowerNull => "",
Power::PowerLight => "LIGHT",
Power::PowerMedium => "MEDIUM",
Power::PowerFull => "FULL",
};
write!(f, "{}", s)
}
}
impl<'de> serde::de::Deserialize<'de> for Power {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
match str::parse(serde::de::Deserialize::deserialize(deserializer)?) {
Ok(p) => Ok(p),
Err(e) => Err(serde::de::Error::custom(e.to_string())),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default, serde::Deserialize)]
#[serde(rename_all = "kebab-case", default)]
pub struct SlotQueueInfo {
pub id: String,
pub state: String,
pub error: String,
pub project: i64,
pub run: i64,
pub clone: i64,
pub gen: i64,
pub core: String,
pub unit: String,
pub percent_done: String,
pub eta: FAHDuration,
pub ppd: StringInt,
pub credit_estimate: StringInt,
pub waiting_on: String,
pub next_attempt: FAHDuration,
pub time_remaining: FAHDuration,
pub total_frames: i64,
pub frames_done: i64,
pub assigned: FAHTime,
pub timeout: FAHTime,
pub deadline: FAHTime,
pub ws: String,
pub cs: String,
pub attempts: i64,
pub slot: String,
pub tpf: FAHDuration,
pub base_credit: StringInt,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct FAHTime(pub Option<chrono::DateTime<chrono::offset::Utc>>);
impl From<Option<chrono::DateTime<chrono::offset::Utc>>> for FAHTime {
fn from(t: Option<chrono::DateTime<chrono::offset::Utc>>) -> Self {
Self(t)
}
}
impl From<chrono::DateTime<chrono::offset::Utc>> for FAHTime {
fn from(t: chrono::DateTime<chrono::offset::Utc>) -> Self {
Self(Some(t))
}
}
const INVALID_TIME: &str = "<invalid>";
impl std::fmt::Display for FAHTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
Some(t) => write!(f, "{}", t),
None => write!(f, "{}", INVALID_TIME),
}
}
}
impl<'de> serde::de::Deserialize<'de> for FAHTime {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = serde::de::Deserialize::deserialize(deserializer)?;
if s == INVALID_TIME {
return Ok(None.into());
}
match chrono::DateTime::parse_from_rfc3339(s) {
Ok(t) => Ok(t.with_timezone(&chrono::offset::Utc).into()),
Err(e) => Err(serde::de::Error::custom(e.to_string())),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct FAHDuration(pub Option<chrono::Duration>);
impl From<Option<chrono::Duration>> for FAHDuration {
fn from(d: Option<chrono::Duration>) -> Self {
Self(d)
}
}
impl From<chrono::Duration> for FAHDuration {
fn from(d: chrono::Duration) -> Self {
Self(Some(d))
}
}
const UNKNOWN_TIME: &str = "unknowntime";
impl std::fmt::Display for FAHDuration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
Some(d) => write!(f, "{}", d),
None => write!(f, "{}", UNKNOWN_TIME),
}
}
}
impl<'de> serde::de::Deserialize<'de> for FAHDuration {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: &str = serde::de::Deserialize::deserialize(deserializer)?;
if s == UNKNOWN_TIME {
return Ok(None.into());
}
if let Some(number_of_days) = s.strip_suffix(" days") {
if number_of_days.contains('.') {
const MILLIS_PER_DAY: f64 = (1000 * 60 * 60 * 24) as f64;
let n = f64::from_str(number_of_days)
.map_err(|e| serde::de::Error::custom(e.to_string()))?;
return Ok(
chrono::Duration::milliseconds((MILLIS_PER_DAY * n).round() as i64).into(),
);
}
}
if let Some(number_of_seconds) = s.strip_suffix(" secs") {
if number_of_seconds.contains('.') {
const MILLIS_PER_SECOND: f64 = 1000.0;
let n = f64::from_str(number_of_seconds)
.map_err(|e| serde::de::Error::custom(e.to_string()))?;
return Ok(
chrono::Duration::milliseconds((MILLIS_PER_SECOND * n).round() as i64).into(),
);
}
}
let duration =
humantime::parse_duration(s).map_err(|e| serde::de::Error::custom(e.to_string()))?;
let result = chrono::Duration::from_std(duration)
.map_err(|e| serde::de::Error::custom(e.to_string()))?;
Ok(result.into())
}
}
#[derive(Clone, PartialEq, Debug, Default, serde::Deserialize)]
#[serde(rename_all = "kebab-case", default)]
pub struct SimulationInfo {
pub user: String,
pub team: String,
pub project: i64,
pub run: i64,
pub clone: i64,
pub gen: i64,
pub core_type: i64,
pub core: String,
pub total_iterations: i64,
pub iterations_done: i64,
pub energy: i64,
pub temperature: i64,
pub start_time: FAHTime,
pub timeout: i64,
pub deadline: i64,
pub eta: i64,
pub progress: f64,
pub slot: i64,
}
#[derive(Clone, PartialEq, Eq, Debug, Default, serde::Deserialize)]
#[serde(rename_all = "kebab-case", default)]
pub struct SlotInfo {
pub id: String,
pub status: String,
pub description: String,
pub options: serde_json::map::Map<String, serde_json::Value>,
pub reason: String,
pub idle: bool,
}
#[derive(Clone, PartialEq, Eq, Debug, Default, serde::Deserialize)]
#[serde(rename_all = "kebab-case", default)]
pub struct SlotOptions {
pub machine_id: String,
pub paused: StringBool,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default, serde::Deserialize)]
pub struct Info {
pub fah_client: FAHClient,
pub cbang: CBang,
pub system: System,
pub libfah: LibFAH,
}
impl Info {
pub fn new(src: Vec<Vec<serde_json::Value>>) -> Result<Self> {
if src.len() < 4 {
return Err(Error::msg(format!(
"src should have 4 arrays but has {}",
src.len()
)));
}
let mut info = Info::default();
for row in src {
let mut row_iter = row.iter();
let field: &mut dyn FieldSetter = match row_iter
.next()
.and_then(|e| e.as_str())
.ok_or_else(|| Error::msg("unexpected type"))?
{
"FAHClient" => &mut info.fah_client,
"CBang" => &mut info.cbang,
"System" => &mut info.system,
"libFAH" => &mut info.libfah,
s => {
eprintln!("unexpected row type: {}", s);
continue;
}
};
for v in row_iter {
let entry = v.as_array().ok_or_else(|| Error::msg("unexpected type"))?;
let k = entry
.get(0)
.and_then(|k| k.as_str())
.ok_or_else(|| Error::msg("unexpected type"))?;
let v = entry
.get(1)
.and_then(|v| v.as_str())
.ok_or_else(|| Error::msg("unexpected type"))?;
field.set(k, v)?;
}
}
Ok(info)
}
}
trait FieldSetter {
fn set(&mut self, k: &str, value: &str) -> Result<()>;
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default, serde::Deserialize)]
pub struct FAHClient {
pub version: String,
pub author: String,
pub copyright: String,
pub homepage: String,
pub date: String,
pub time: String,
pub revision: String,
pub branch: String,
pub compiler: String,
pub options: String,
pub platform: String,
pub bits: String,
pub mode: String,
pub args: String,
pub config: String,
}
impl FieldSetter for FAHClient {
fn set(&mut self, k: &str, value: &str) -> Result<()> {
let v = value.to_string();
match k {
"Version" => self.version = v,
"Author" => self.author = v,
"Copyright" => self.copyright = v,
"Homepage" => self.homepage = v,
"Date" => self.date = v,
"Time" => self.time = v,
"Revision" => self.revision = v,
"Branch" => self.branch = v,
"Compiler" => self.compiler = v,
"Options" => self.options = v,
"Platform" => self.platform = v,
"Bits" => self.bits = v,
"Mode" => self.mode = v,
"Args" => self.args = v,
"Config" => self.config = v,
_ => eprintln!("discarded unknown field: {}", k),
};
Ok(())
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default, serde::Deserialize)]
pub struct CBang {
pub date: String,
pub time: String,
pub revision: String,
pub branch: String,
pub compiler: String,
pub options: String,
pub platform: String,
pub bits: String,
pub mode: String,
}
impl FieldSetter for CBang {
fn set(&mut self, k: &str, value: &str) -> Result<()> {
let v = value.to_string();
match k {
"Date" => self.date = v,
"Time" => self.time = v,
"Revision" => self.revision = v,
"Branch" => self.branch = v,
"Compiler" => self.compiler = v,
"Options" => self.options = v,
"Platform" => self.platform = v,
"Bits" => self.bits = v,
"Mode" => self.mode = v,
_ => eprintln!("discarded unknown field: {}", k),
};
Ok(())
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default, serde::Deserialize)]
pub struct System {
pub cpu: String,
pub cpu_id: String,
pub cpus: StringInt,
pub memory: String,
pub free_memory: String,
pub threads: String,
pub os_version: String,
pub has_battery: String,
pub on_battery: String,
pub utc_offset: String,
pub pid: String,
pub cwd: String,
pub os: String,
pub os_arch: String,
pub gpus: StringInt,
}
impl FieldSetter for System {
fn set(&mut self, k: &str, value: &str) -> Result<()> {
let v = value.to_string();
match k {
"CPU" => self.cpu = v,
"CPU ID" => self.cpu_id = v,
"CPUs" => self.cpus = str::parse(value)?,
"Memory" => self.memory = v,
"Free Memory" => self.free_memory = v,
"Threads" => self.threads = v,
"OS Version" => self.os_version = v,
"Has Battery" => self.has_battery = v,
"On Battery" => self.on_battery = v,
"UTC Offset" => self.utc_offset = v,
"PID" => self.pid = v,
"CWD" => self.cwd = v,
"OS" => self.os = v,
"OS Arch" => self.os_arch = v,
"GPUs" => self.gpus = str::parse(value)?,
_ => {
if !k.starts_with("GPU") && !k.starts_with("CUDA") && !k.starts_with("OpenCL") {
eprintln!("discarded unknown field: {}", k);
}
}
};
Ok(())
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Default, serde::Deserialize)]
pub struct LibFAH {
pub date: String,
pub time: String,
pub revision: String,
pub branch: String,
pub compiler: String,
pub options: String,
pub platform: String,
pub bits: String,
pub mode: String,
}
impl FieldSetter for LibFAH {
fn set(&mut self, k: &str, value: &str) -> Result<()> {
let v = value.to_string();
match k {
"Date" => self.date = v,
"Time" => self.time = v,
"Revision" => self.revision = v,
"Branch" => self.branch = v,
"Compiler" => self.compiler = v,
"Options" => self.options = v,
"Platform" => self.platform = v,
"Bits" => self.bits = v,
"Mode" => self.mode = v,
_ => eprintln!("discarded unknown field: {}", k),
};
Ok(())
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_info_new() {
let src = vec![
vec![
serde_json::Value::String("FAHClient".into()),
serde_json::Value::Array(vec!["Version".into(), "7.6.13".into()]),
],
vec![
serde_json::Value::String("CBang".into()),
serde_json::Value::Array(vec!["Date".into(), "Apr 20 2020".into()]),
],
vec![
serde_json::Value::String("System".into()),
serde_json::Value::Array(vec![
"CPU ID".into(),
"Intel Management Engine is a backdoor".into(),
]),
serde_json::Value::Array(vec!["CPUs".into(), "1".into()]),
],
vec![
serde_json::Value::String("libFAH".into()),
serde_json::Value::Array(vec!["Date".into(), "Apr 20 2020".into()]),
],
];
assert!(Info::new(Vec::new()).is_err());
let result = Info::new(src).unwrap();
assert!(!result.fah_client.version.is_empty());
assert!(!result.system.cpu_id.is_empty());
assert_eq!(result.system.cpus, StringInt(1));
}
#[test]
fn test_fahduration_deserialize() {
let s = r#""0.00 secs""#;
let result: FAHDuration = serde_json::from_str(s).unwrap();
assert_eq!(result.0.unwrap().num_seconds(), 0);
let s = r#""1 days""#;
let result: FAHDuration = serde_json::from_str(s).unwrap();
assert_eq!(result.0.unwrap().num_days(), 1);
let s = r#""1.1 days""#;
let result: FAHDuration = serde_json::from_str(s).unwrap();
assert_eq!(result.0.unwrap().num_days(), 1);
assert_eq!(result.0.unwrap().num_milliseconds(), 95040000);
}
}