use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub struct DiagnosticEvent {
pub gateway_version: i32,
pub error_code: i32,
pub error_message: String,
pub error_time: Option<time::OffsetDateTime>,
pub farm_status: FarmState,
pub connection_state: ConnectionState,
pub account_type: AccountType,
pub os: &'static str,
pub timestamp: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FarmState {
Ok,
Warning,
Inactive,
Unknown(i32),
}
impl std::fmt::Display for FarmState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FarmState::Ok => write!(f, "ok"),
FarmState::Warning => write!(f, "warning"),
FarmState::Inactive => write!(f, "inactive"),
FarmState::Unknown(code) => write!(f, "unknown({code})"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionState {
Connected,
Disconnected,
Reconnecting,
}
impl std::fmt::Display for ConnectionState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConnectionState::Connected => write!(f, "connected"),
ConnectionState::Disconnected => write!(f, "disconnected"),
ConnectionState::Reconnecting => write!(f, "reconnecting"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AccountType {
Live,
Paper,
}
impl std::fmt::Display for AccountType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AccountType::Live => write!(f, "live"),
AccountType::Paper => write!(f, "paper"),
}
}
}
pub fn classify_farm(code: i32) -> FarmState {
match code {
2104 | 2106 => FarmState::Ok,
2105 | 2108 => FarmState::Warning,
2107 => FarmState::Inactive,
other => FarmState::Unknown(other),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[test]
fn farm_2104_is_ok() {
assert_eq!(classify_farm(2104), FarmState::Ok);
}
#[test]
fn farm_2106_is_ok() {
assert_eq!(classify_farm(2106), FarmState::Ok);
}
#[test]
fn farm_2105_is_warning() {
assert_eq!(classify_farm(2105), FarmState::Warning);
}
#[test]
fn farm_2108_is_warning() {
assert_eq!(classify_farm(2108), FarmState::Warning);
}
#[test]
fn farm_2107_is_inactive() {
assert_eq!(classify_farm(2107), FarmState::Inactive);
}
#[test]
fn farm_999_is_unknown() {
assert_eq!(classify_farm(999), FarmState::Unknown(999));
}
#[test]
fn farm_0_is_unknown() {
assert_eq!(classify_farm(0), FarmState::Unknown(0));
}
#[test]
fn farm_state_display_ok() {
assert_eq!(FarmState::Ok.to_string(), "ok");
}
#[test]
fn farm_state_display_warning() {
assert_eq!(FarmState::Warning.to_string(), "warning");
}
#[test]
fn farm_state_display_inactive() {
assert_eq!(FarmState::Inactive.to_string(), "inactive");
}
#[test]
fn farm_state_display_unknown() {
assert_eq!(FarmState::Unknown(42).to_string(), "unknown(42)");
}
#[test]
fn connection_state_display_connected() {
assert_eq!(ConnectionState::Connected.to_string(), "connected");
}
#[test]
fn connection_state_display_disconnected() {
assert_eq!(ConnectionState::Disconnected.to_string(), "disconnected");
}
#[test]
fn connection_state_display_reconnecting() {
assert_eq!(ConnectionState::Reconnecting.to_string(), "reconnecting");
}
#[test]
fn account_type_display_live() {
assert_eq!(AccountType::Live.to_string(), "live");
}
#[test]
fn account_type_display_paper() {
assert_eq!(AccountType::Paper.to_string(), "paper");
}
#[test]
fn diagnostic_event_construct() {
let event = DiagnosticEvent {
gateway_version: 221,
error_code: 2104,
error_message: "Market data farm connection OK".into(),
error_time: None,
farm_status: FarmState::Ok,
connection_state: ConnectionState::Connected,
account_type: AccountType::Paper,
os: std::env::consts::OS,
timestamp: chrono::Utc::now(),
};
assert_eq!(event.gateway_version, 221);
assert_eq!(event.error_code, 2104);
assert_eq!(event.farm_status, FarmState::Ok);
assert_eq!(event.connection_state, ConnectionState::Connected);
assert_eq!(event.account_type, AccountType::Paper);
assert_eq!(event.os, std::env::consts::OS);
}
#[test]
fn farm_state_ok_serializes_to_json() {
let json = serde_json::to_string(&FarmState::Ok).unwrap();
assert_eq!(json, "\"Ok\"");
}
#[test]
fn farm_state_warning_serializes_to_json() {
let json = serde_json::to_string(&FarmState::Warning).unwrap();
assert_eq!(json, "\"Warning\"");
}
#[test]
fn farm_state_inactive_serializes_to_json() {
let json = serde_json::to_string(&FarmState::Inactive).unwrap();
assert_eq!(json, "\"Inactive\"");
}
#[test]
fn farm_state_unknown_serializes_to_json_object() {
let json = serde_json::to_string(&FarmState::Unknown(10197)).unwrap();
assert_eq!(json, "{\"Unknown\":10197}");
}
#[test]
fn farm_state_ok_deserializes_from_json() {
let state: FarmState = serde_json::from_str("\"Ok\"").unwrap();
assert_eq!(state, FarmState::Ok);
}
#[test]
fn farm_state_unknown_deserializes_from_json_object() {
let state: FarmState = serde_json::from_str("{\"Unknown\":10197}").unwrap();
assert_eq!(state, FarmState::Unknown(10197));
}
#[test]
fn farm_state_round_trips_ok() {
for (original, expected_json) in &[
(FarmState::Ok, "\"Ok\""),
(FarmState::Warning, "\"Warning\""),
(FarmState::Inactive, "\"Inactive\""),
(FarmState::Unknown(0), "{\"Unknown\":0}"),
(FarmState::Unknown(2107), "{\"Unknown\":2107}"),
(FarmState::Unknown(9999), "{\"Unknown\":9999}"),
] {
let json = serde_json::to_string(original).unwrap();
assert_eq!(&json, expected_json, "serialize failed for {original:?}");
let deserialized: FarmState = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, *original, "deserialize failed for {original:?}");
}
}
}