use std::fmt::Debug;
use std::fmt::Display;
use crate::GCAbiConversion;
use crate::GCDatapointID;
use crate::GCDatapointValueQuality;
use crate::error::GCPluginABIError;
use gc_abi_sys::sGCDatapointValue__bindgen_ty_1 as BindagenValue;
use serde::Deserializer;
use serde::ser::{SerializeStruct, Serializer};
use serde::{Deserialize, Serialize};
pub type GCTimestamp = u64;
#[derive(PartialEq, Debug, Deserialize, Serialize)]
#[serde(tag = "type", content = "data")]
pub enum GCDatapointValueType {
Bool(bool),
DoublePoint(u8),
Int64(i64),
Int32(i32),
UInt32(u32),
UInt64(u64),
Double(f64),
Float(f32),
Bytes(Vec<u8>),
TimestampNs(GCTimestamp),
}
impl Display for GCDatapointValueType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GCDatapointValueType::Bool(v) => write!(f, "{}", v),
GCDatapointValueType::DoublePoint(v) => write!(f, "{}", v),
GCDatapointValueType::Int64(v) => write!(f, "{}", v),
GCDatapointValueType::Int32(v) => write!(f, "{}", v),
GCDatapointValueType::UInt32(v) => write!(f, "{}", v),
GCDatapointValueType::UInt64(v) => write!(f, "{}", v),
GCDatapointValueType::Double(v) => write!(f, "{}", v),
GCDatapointValueType::Float(v) => write!(f, "{}", v),
GCDatapointValueType::Bytes(v) => write!(f, "{:?})", v),
GCDatapointValueType::TimestampNs(v) => write!(f, "{}", v),
}
}
}
#[repr(transparent)]
#[derive(Clone)]
pub struct GCDatapointValue(gc_abi_sys::sGCDatapointValue);
impl GCDatapointValue {
pub fn new_boolean(datapoint_id: GCDatapointID, timestamp_ns: GCTimestamp, value: bool, quality: GCDatapointValueQuality) -> Self {
GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id,
type_: gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_BOOLEAN,
timestamp: timestamp_ns,
value: BindagenValue { b: value },
quality: quality.into_c(),
})
}
pub fn new_timestamp_ns(datapoint_id: GCDatapointID, timestamp_ns: GCTimestamp, value: GCTimestamp, quality: GCDatapointValueQuality) -> Self {
GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id,
type_: gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_TIMESTAMPNS,
timestamp: timestamp_ns,
value: BindagenValue { timestampNs: value },
quality: quality.into_c(),
})
}
pub fn new_u64(datapoint_id: GCDatapointID, timestamp_ns: GCTimestamp, value: u64, quality: GCDatapointValueQuality) -> Self {
GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id,
type_: gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_UINT64,
timestamp: timestamp_ns,
value: BindagenValue { u64_: value },
quality: quality.into_c(),
})
}
pub fn new_u32(datapoint_id: GCDatapointID, timestamp_ns: GCTimestamp, value: u32, quality: GCDatapointValueQuality) -> Self {
GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id,
type_: gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_UINT32,
timestamp: timestamp_ns,
value: BindagenValue { u32_: value },
quality: quality.into_c(),
})
}
pub fn new_i64(datapoint_id: GCDatapointID, timestamp_ns: GCTimestamp, value: i64, quality: GCDatapointValueQuality) -> Self {
GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id,
type_: gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_INT64,
timestamp: timestamp_ns,
value: BindagenValue { i64_: value },
quality: quality.into_c(),
})
}
pub fn new_i32(datapoint_id: GCDatapointID, timestamp_ns: GCTimestamp, value: i32, quality: GCDatapointValueQuality) -> Self {
GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id,
type_: gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_INT32,
timestamp: timestamp_ns,
value: BindagenValue { i32_: value },
quality: quality.into_c(),
})
}
pub fn new_f64(datapoint_id: GCDatapointID, timestamp_ns: GCTimestamp, value: f64, quality: GCDatapointValueQuality) -> Self {
GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id,
type_: gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_FLOAT64,
timestamp: timestamp_ns,
value: BindagenValue { f64_: value },
quality: quality.into_c(),
})
}
pub fn new_f32(datapoint_id: GCDatapointID, timestamp_ns: GCTimestamp, value: f32, quality: GCDatapointValueQuality) -> Self {
GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id,
type_: gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_FLOAT32,
timestamp: timestamp_ns,
value: BindagenValue { f32_: value },
quality: quality.into_c(),
})
}
pub fn new_double_point(datapoint_id: GCDatapointID, timestamp_ns: GCTimestamp, value: u8, quality: GCDatapointValueQuality) -> Self {
GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id,
type_: gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_DOUBLEPOINT,
timestamp: timestamp_ns,
value: BindagenValue { dp: value },
quality: quality.into_c(),
})
}
pub fn new_byte(datapoint_id: GCDatapointID, timestamp_ns: GCTimestamp, value: bool, quality: GCDatapointValueQuality) -> Self {
GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id,
type_: gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_BOOLEAN,
timestamp: timestamp_ns,
value: BindagenValue { b: value },
quality: quality.into_c(),
})
}
#[inline]
pub fn get_value(&self) -> Result<GCDatapointValueType, GCPluginABIError> {
match self.0.type_ {
gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_FLOAT32 => Ok(GCDatapointValueType::Float(unsafe { self.0.value.f32_ })),
gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_FLOAT64 => Ok(GCDatapointValueType::Double(unsafe { self.0.value.f64_ })),
gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_INT32 => Ok(GCDatapointValueType::Int32(unsafe { self.0.value.i32_ })),
gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_INT64 => Ok(GCDatapointValueType::Int64(unsafe { self.0.value.i64_ })),
gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_UINT32 => Ok(GCDatapointValueType::UInt32(unsafe { self.0.value.u32_ })),
gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_UINT64 => Ok(GCDatapointValueType::UInt64(unsafe { self.0.value.u64_ })),
gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_BOOLEAN => Ok(GCDatapointValueType::Bool(unsafe { self.0.value.b })),
gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_DOUBLEPOINT => Ok(GCDatapointValueType::DoublePoint(unsafe { self.0.value.dp })),
gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_TIMESTAMPNS => {
Ok(GCDatapointValueType::TimestampNs(unsafe { self.0.value.timestampNs }))
}
gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_BYTE_ARRAY => Err(GCPluginABIError::UnsuportedType(String::from("Byte array"))),
unknown => Err(GCPluginABIError::UnknownDatapointValue(unknown)),
}
}
#[inline]
pub fn set_value(&mut self, value: GCDatapointValueType) {
self.0.value = match value {
GCDatapointValueType::Double(v) => BindagenValue { f64_: v },
GCDatapointValueType::UInt64(v) => BindagenValue { u64_: v },
GCDatapointValueType::Int64(v) => BindagenValue { i64_: v },
GCDatapointValueType::Int32(v) => BindagenValue { i32_: v },
GCDatapointValueType::UInt32(v) => BindagenValue { u32_: v },
GCDatapointValueType::Float(v) => BindagenValue { f32_: v },
GCDatapointValueType::Bool(v) => BindagenValue { b: v },
GCDatapointValueType::DoublePoint(v) => BindagenValue { dp: v },
GCDatapointValueType::TimestampNs(v) => BindagenValue { timestampNs: v },
GCDatapointValueType::Bytes(_) => panic!("Byte array not supported"),
};
}
#[inline]
pub fn get_value_bool(&self) -> Option<bool> {
if self.0.type_ == gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_BOOLEAN {
return Some(unsafe { self.0.value.b });
}
None
}
#[inline]
pub fn get_value_double_point(&self) -> Option<u8> {
if self.0.type_ == gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_DOUBLEPOINT {
return Some(unsafe { self.0.value.dp });
}
None
}
#[inline]
pub fn get_value_i64(&self) -> Option<i64> {
if self.0.type_ == gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_INT64 {
return Some(unsafe { self.0.value.i64_ });
}
None
}
#[inline]
pub fn get_value_i32(&self) -> Option<i32> {
if self.0.type_ == gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_INT32 {
return Some(unsafe { self.0.value.i32_ });
}
None
}
#[inline]
pub fn get_value_u32(&self) -> Option<u32> {
if self.0.type_ == gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_UINT32 {
return Some(unsafe { self.0.value.u32_ });
}
None
}
#[inline]
pub fn get_value_u64(&self) -> Option<u64> {
if self.0.type_ == gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_UINT64 {
return Some(unsafe { self.0.value.u64_ });
}
None
}
#[inline]
pub fn get_value_f64(&self) -> Option<f64> {
if self.0.type_ == gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_FLOAT64 {
return Some(unsafe { self.0.value.f64_ });
}
None
}
#[inline]
pub fn get_value_f32(&self) -> Option<f32> {
if self.0.type_ == gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_FLOAT32 {
return Some(unsafe { self.0.value.f32_ });
}
None
}
#[inline]
pub fn get_value_timestamp_ns(&self) -> Option<GCTimestamp> {
if self.0.type_ == gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_TIMESTAMPNS {
return Some(unsafe { self.0.value.timestampNs });
}
None
}
#[inline]
pub fn get_value_bytes(&self) -> Option<Vec<u8>> {
None
}
#[inline(always)]
pub fn get_timestamp(&self) -> GCTimestamp {
self.0.timestamp
}
#[inline(always)]
pub fn get_id(&self) -> GCDatapointID {
self.0.datapoint_id
}
#[inline(always)]
pub fn get_quality(&self) -> GCDatapointValueQuality {
GCDatapointValueQuality::from_c(self.0.quality)
}
#[inline]
pub fn as_raw(&self) -> gc_abi_sys::sGCDatapointValue {
self.0
}
}
unsafe impl GCAbiConversion for GCDatapointValue {
type CType = gc_abi_sys::sGCDatapointValue;
fn from_c(c_type: Self::CType) -> Self {
GCDatapointValue(c_type)
}
fn into_c(self) -> Self::CType {
self.0
}
}
impl Debug for GCDatapointValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GCDatapointValue")
.field("value", &self.get_value())
.field("timestamp", &self.get_timestamp())
.field("id", &self.get_id())
.field("quality", &self.get_quality())
.finish()
}
}
impl Default for GCDatapointValue {
fn default() -> Self {
GCDatapointValue::new_i32(0, 0, 0, GCDatapointValueQuality::new_good())
}
}
impl serde::ser::Serialize for GCDatapointValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let binding = self.get_value();
let value = match &binding {
Err(e) => Err(serde::ser::Error::custom(e.to_string())),
Ok(v) => Ok(v),
}?;
let mut state = serializer.serialize_struct("GCDatapointValue", 4)?;
state.serialize_field("value", value)?;
state.serialize_field("timestamp", &self.get_timestamp())?;
state.serialize_field("id", &self.get_id())?;
state.serialize_field("quality", &self.get_quality())?;
state.end()
}
}
impl<'de> Deserialize<'de> for GCDatapointValue {
fn deserialize<D>(deserializer: D) -> Result<GCDatapointValue, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct GCDatapointValueHelper {
value: GCDatapointValueType,
timestamp: GCTimestamp,
id: GCDatapointID,
quality: GCDatapointValueQuality,
}
let helper = GCDatapointValueHelper::deserialize(deserializer)?;
let (value, type_) = match helper.value {
GCDatapointValueType::Bool(v) => (BindagenValue { b: v }, gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_BOOLEAN),
GCDatapointValueType::DoublePoint(v) => (BindagenValue { dp: v }, gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_DOUBLEPOINT),
GCDatapointValueType::Int64(v) => (BindagenValue { i64_: v }, gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_INT64),
GCDatapointValueType::Int32(v) => (BindagenValue { i32_: v }, gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_INT32),
GCDatapointValueType::UInt32(v) => (BindagenValue { u32_: v }, gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_UINT32),
GCDatapointValueType::UInt64(v) => (BindagenValue { u64_: v }, gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_UINT64),
GCDatapointValueType::Double(v) => (BindagenValue { f64_: v }, gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_FLOAT64),
GCDatapointValueType::Float(v) => (BindagenValue { f32_: v }, gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_FLOAT32),
GCDatapointValueType::TimestampNs(v) => (
BindagenValue { timestampNs: v },
gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_TIMESTAMPNS,
),
GCDatapointValueType::Bytes(_) => return Err(serde::de::Error::custom("Byte array not supported")),
};
Ok(GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id: helper.id,
type_,
timestamp: helper.timestamp,
value,
quality: helper.quality.into_c(),
}))
}
}
impl PartialEq for GCDatapointValue {
fn eq(&self, other: &Self) -> bool {
let result = self.get_id() == other.get_id() && self.get_timestamp() == other.get_timestamp() && self.get_quality() == other.get_quality();
if !result {
return false;
}
if let Ok(self_value) = self.get_value() {
if let Ok(other_value) = other.get_value() {
return self_value == other_value;
}
return false;
}
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ptr;
#[test]
fn test_int_values() {
let val_i32 = GCDatapointValue::new_i32(0, 10, 1234, GCDatapointValueQuality::new_good());
assert_eq!(GCDatapointValueType::Int32(1234), val_i32.get_value().unwrap());
assert_eq!(1234, val_i32.get_value_i32().unwrap());
let val_i64 = GCDatapointValue::new_i64(0, 10, -1234567891011, GCDatapointValueQuality::new_good());
assert_eq!(GCDatapointValueType::Int64(-1234567891011), val_i64.get_value().unwrap());
assert_eq!(-1234567891011, val_i64.get_value_i64().unwrap());
let val_u32 = GCDatapointValue::new_u32(0, 10, 12345, GCDatapointValueQuality::new_good());
assert_eq!(GCDatapointValueType::UInt32(12345), val_u32.get_value().unwrap());
assert_eq!(12345, val_u32.get_value_u32().unwrap());
let val_u64 = GCDatapointValue::new_u64(0, 10, 1234567891011, GCDatapointValueQuality::new_good());
assert_eq!(GCDatapointValueType::UInt64(1234567891011), val_u64.get_value().unwrap());
assert_eq!(1234567891011, val_u64.get_value_u64().unwrap());
}
#[test]
fn test_float_values() {
let val_f32 = GCDatapointValue::new_f32(0, 10, 1234.0, GCDatapointValueQuality::new_good());
assert_eq!(GCDatapointValueType::Float(1234.0), val_f32.get_value().unwrap());
assert_eq!(1234.0, val_f32.get_value_f32().unwrap());
let val_f64 = GCDatapointValue::new_f64(0, 10, -1234567891011.0, GCDatapointValueQuality::new_good());
assert_eq!(GCDatapointValueType::Double(-1234567891011.0), val_f64.get_value().unwrap());
assert_eq!(-1234567891011.0, val_f64.get_value_f64().unwrap());
}
#[test]
fn test_bool_value() {
let val = GCDatapointValue::new_byte(0, 10, true, GCDatapointValueQuality::new_good());
assert_eq!(GCDatapointValueType::Bool(true), val.get_value().unwrap());
assert!(val.get_value_bool().unwrap());
}
#[test]
fn test_dp_value() {
let val = GCDatapointValue::new_double_point(0, 10, 23, GCDatapointValueQuality::new_good());
assert_eq!(GCDatapointValueType::DoublePoint(23), val.get_value().unwrap());
assert_eq!(23, val.get_value_double_point().unwrap());
}
#[test]
fn test_quality() {
let mut quality = GCDatapointValueQuality::new_good();
quality.set_old_data();
let datapoint_value: GCDatapointValue = GCDatapointValue::new_i32(0, 10, 1234, quality);
assert!(datapoint_value.get_quality().is_good());
assert!(datapoint_value.get_quality().is_old_data());
assert!(!datapoint_value.get_quality().is_invalid());
}
#[test]
fn test_value_timestamp() {
let val = GCDatapointValue::new_timestamp_ns(0, 10, 1234, GCDatapointValueQuality::new_good());
assert_eq!(1234, val.get_value_timestamp_ns().unwrap());
assert_eq!(GCDatapointValueType::TimestampNs(1234), val.get_value().unwrap());
let val = GCDatapointValue::new_i32(0, 0, 1234, GCDatapointValueQuality::new_good());
assert_eq!(None, val.get_value_timestamp_ns());
}
#[test]
fn test_unknown_value() {
let val = GCDatapointValue(gc_abi_sys::sGCDatapointValue {
type_: 100,
value: BindagenValue { dp: 23 },
timestamp: 10,
datapoint_id: 0,
quality: 0,
});
assert!(matches!(val.get_value(), Err(GCPluginABIError::UnknownDatapointValue(100))));
}
#[test]
fn test_clone() {
let alloc_info = allocation_counter::measure(|| {
let dp = Box::new(GCDatapointValue::new_i32(0, 10, 1234, GCDatapointValueQuality::new_good()));
let dp_clone = dp.clone();
drop(dp);
assert_eq!(dp_clone.get_id(), 0);
assert_eq!(dp_clone.get_timestamp(), 10);
assert!(dp_clone.get_quality().is_good());
assert_eq!(dp_clone.get_value_i32().unwrap(), 1234);
});
assert_eq!(alloc_info.bytes_current, 0, "Memory leak detected");
}
#[test]
fn test_conversion_to_c() {
let alloc_info = allocation_counter::measure(|| {
let dp = Box::new(GCDatapointValue::new_i32(0, 10, 1234, GCDatapointValueQuality::new_good()));
unsafe {
let raw_dp_ptr = Box::into_raw(dp);
let raw_dp = raw_dp_ptr.as_mut().unwrap();
let c_dp: *mut gc_abi_sys::sGCDatapointValue = raw_dp.as_mut_ptr();
let from_c_dp: &GCDatapointValue = GCDatapointValue::from_c_ptr(c_dp);
let from_c_dp_mut: &GCDatapointValue = GCDatapointValue::from_c_ptr(c_dp as *const gc_abi_sys::sGCDatapointValue);
assert!(ptr::addr_eq(from_c_dp, raw_dp_ptr), "The rust abstraction should be zero cost");
assert!(ptr::addr_eq(from_c_dp_mut, raw_dp_ptr), "The rust abstraction should be zero cost");
assert!(ptr::addr_eq(c_dp, raw_dp_ptr), "The rust abstraction should be zero cost");
let dp = Box::from_raw(raw_dp_ptr);
assert_eq!(dp.get_id(), 0);
assert_eq!(dp.get_timestamp(), 10);
assert!(dp.get_quality().is_good());
assert_eq!(dp.get_value_i32().unwrap(), 1234);
}
});
assert_eq!(alloc_info.bytes_current, 0, "Memory leak detected");
assert_eq!(
alloc_info.count_total, 1,
"Additional memory allocation detected, the conversion shouldn't allocate any memory"
);
}
#[test]
fn test_pointer_ref_conversion() {
let alloc_info = allocation_counter::measure(|| {
let dp = Box::new(gc_abi_sys::sGCDatapointValue {
datapoint_id: 0,
type_: gc_abi_sys::eGCDatapointValueType_GCOS_DATAPOINT_TYPE_INT32,
timestamp: 10,
value: BindagenValue { i32_: 1234 },
quality: GCDatapointValueQuality::new_good().into_c(),
});
unsafe {
let raw_dp_ptr = Box::into_raw(dp);
let converted: &GCDatapointValue = GCDatapointValue::from_c_ptr(raw_dp_ptr);
assert!(ptr::addr_eq(converted, raw_dp_ptr), "The rust abstraction should be zero cost");
assert_eq!(converted.get_id(), 0);
assert_eq!(converted.get_timestamp(), 10);
assert!(converted.get_quality().is_good());
assert_eq!(converted.get_value_i32().unwrap(), 1234);
drop(Box::from_raw(raw_dp_ptr));
}
});
assert_eq!(alloc_info.bytes_current, 0, "Memory leak detected");
assert_eq!(
alloc_info.count_total, 1,
"Additional memory allocation detected, the conversion shouldn't allocate any memory"
);
}
#[test]
fn test_serialize_deserialize() {
let val = GCDatapointValue::new_i32(0, 10, 1234, GCDatapointValueQuality::new_good());
let serialized = serde_json::to_string(&val).unwrap();
let deserialized: GCDatapointValue = serde_json::from_str(&serialized).unwrap();
assert_eq!(val.get_id(), deserialized.get_id());
assert_eq!(val.get_timestamp(), deserialized.get_timestamp());
assert_eq!(val.get_quality(), deserialized.get_quality());
assert_eq!(val.get_value_i32().unwrap(), deserialized.get_value_i32().unwrap());
}
#[test]
fn test_serialize_deserialize_bool() {
let val = GCDatapointValue::new_boolean(0, 10, true, GCDatapointValueQuality::new_good());
let serialized = serde_json::to_string(&val).unwrap();
let deserialized: GCDatapointValue = serde_json::from_str(&serialized).unwrap();
assert_eq!(val.get_id(), deserialized.get_id());
assert_eq!(val.get_timestamp(), deserialized.get_timestamp());
assert_eq!(val.get_quality(), deserialized.get_quality());
assert_eq!(val.get_value_bool().unwrap(), deserialized.get_value_bool().unwrap());
}
#[test]
fn test_serialize_deserialize_double_point() {
let val = GCDatapointValue::new_double_point(0, 10, 23, GCDatapointValueQuality::new_good());
let serialized = serde_json::to_string(&val).unwrap();
let deserialized: GCDatapointValue = serde_json::from_str(&serialized).unwrap();
assert_eq!(val.get_id(), deserialized.get_id());
assert_eq!(val.get_timestamp(), deserialized.get_timestamp());
assert_eq!(val.get_quality(), deserialized.get_quality());
assert_eq!(val.get_value_double_point().unwrap(), deserialized.get_value_double_point().unwrap());
}
#[test]
fn test_clone_no_leak() {
let alloc_info = allocation_counter::measure(|| {
let dp = Box::new(GCDatapointValue::default());
let dp_clone = dp.clone();
drop(dp);
assert_eq!(dp_clone.get_id(), 0);
assert_eq!(dp_clone.get_timestamp(), 0);
assert!(dp_clone.get_quality().is_good());
assert_eq!(dp_clone.get_value_i32().unwrap(), 0);
});
assert_eq!(alloc_info.bytes_current, 0, "Memory leak detected");
}
#[test]
fn test_comparison() {
let val1 = GCDatapointValue::new_i32(0, 10, 1234, GCDatapointValueQuality::new_good());
let val2 = GCDatapointValue::new_i32(0, 10, 1234, GCDatapointValueQuality::new_good());
let val3 = GCDatapointValue::new_i32(0, 10, 5678, GCDatapointValueQuality::new_good());
let val4 = GCDatapointValue::new_i32(1, 10, 1234, GCDatapointValueQuality::new_good());
assert_eq!(val1, val2);
assert_ne!(val1, val3);
assert_ne!(val1, val4);
}
#[test]
fn test_comparison_different_types() {
let val1 = GCDatapointValue::new_i32(0, 10, 1234, GCDatapointValueQuality::new_good());
let val2 = GCDatapointValue::new_f32(0, 10, 1234.0, GCDatapointValueQuality::new_good());
assert_ne!(val1, val2);
}
#[test]
fn test_comparison_different_quality() {
let val1 = GCDatapointValue::new_i32(0, 10, 1234, GCDatapointValueQuality::new_good());
let mut quality = GCDatapointValueQuality::new_good();
quality.set_old_data();
let val2 = GCDatapointValue::new_i32(0, 10, 1234, quality);
assert_ne!(val1, val2);
}
#[test]
fn test_comparison_different_timestamp() {
let val1 = GCDatapointValue::new_i32(0, 10, 1234, GCDatapointValueQuality::new_good());
let val2 = GCDatapointValue::new_i32(0, 20, 1234, GCDatapointValueQuality::new_good());
assert_ne!(val1, val2);
}
#[test]
fn test_comparison_different_id() {
let val1 = GCDatapointValue::new_i32(0, 10, 1234, GCDatapointValueQuality::new_good());
let val2 = GCDatapointValue::new_i32(1, 10, 1234, GCDatapointValueQuality::new_good());
assert_ne!(val1, val2);
}
#[test]
fn test_comparison_error() {
let val1 = GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id: 1,
type_: 90,
timestamp: 10,
value: BindagenValue { b: false },
quality: 0,
});
let val2 = GCDatapointValue(gc_abi_sys::sGCDatapointValue {
datapoint_id: 1,
type_: 90,
timestamp: 10,
value: BindagenValue { b: false },
quality: 0,
});
let val3 = GCDatapointValue::new_i32(1, 10, 1234, GCDatapointValueQuality::new_good());
assert_ne!(val1, val2);
assert_ne!(val1, val3);
}
}