use bytes::Bytes;
use crate::datatype::DataType;
use crate::error::{Result, SparkplugError};
use crate::value::{DataSetValue, MetricValue, ParameterValue, PropertyValue};
#[derive(Clone, Debug, PartialEq, Default)]
pub struct Payload {
pub timestamp: Option<u64>,
pub metrics: Vec<Metric>,
pub seq: Option<u8>,
pub uuid: Option<String>,
pub body: Option<Bytes>,
}
impl Payload {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_timestamp(mut self, ts: u64) -> Self {
self.timestamp = Some(ts);
self
}
#[must_use]
pub fn with_seq(mut self, seq: u8) -> Self {
self.seq = Some(seq);
self
}
#[must_use]
pub fn with_metric(mut self, metric: Metric) -> Self {
self.metrics.push(metric);
self
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Metric {
pub name: Option<String>,
pub alias: Option<u64>,
pub timestamp: Option<u64>,
pub value: MetricValue,
pub is_historical: Option<bool>,
pub is_transient: Option<bool>,
pub metadata: Option<MetaData>,
pub properties: Option<PropertySet>,
}
impl Metric {
#[must_use]
pub fn new(name: impl Into<String>, value: MetricValue) -> Self {
Self {
name: Some(name.into()),
alias: None,
timestamp: None,
value,
is_historical: None,
is_transient: None,
metadata: None,
properties: None,
}
}
#[must_use]
pub fn aliased(alias: u64, value: MetricValue) -> Self {
Self {
name: None,
alias: Some(alias),
timestamp: None,
value,
is_historical: None,
is_transient: None,
metadata: None,
properties: None,
}
}
#[must_use]
pub fn with_alias(mut self, alias: u64) -> Self {
self.alias = Some(alias);
self
}
#[must_use]
pub fn with_timestamp(mut self, ts: u64) -> Self {
self.timestamp = Some(ts);
self
}
#[must_use]
pub fn with_properties(mut self, props: PropertySet) -> Self {
self.properties = Some(props);
self
}
#[must_use]
pub fn datatype(&self) -> DataType {
self.value.datatype()
}
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct MetaData {
pub is_multi_part: Option<bool>,
pub content_type: Option<String>,
pub size: Option<u64>,
pub seq: Option<u64>,
pub file_name: Option<String>,
pub file_type: Option<String>,
pub md5: Option<String>,
pub description: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct PropertySet {
pub entries: Vec<(String, PropertyValue)>,
}
impl PropertySet {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with(mut self, key: impl Into<String>, value: PropertyValue) -> Self {
self.entries.push((key.into(), value));
self
}
#[must_use]
pub fn get(&self, key: &str) -> Option<&PropertyValue> {
self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct PropertySetList {
pub sets: Vec<PropertySet>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct DataSet {
columns: Vec<String>,
types: Vec<DataType>,
rows: Vec<Vec<DataSetValue>>,
}
impl DataSet {
pub fn new(
columns: Vec<String>,
types: Vec<DataType>,
rows: Vec<Vec<DataSetValue>>,
) -> Result<Self> {
if columns.len() != types.len() {
return Err(SparkplugError::DataSetShape(format!(
"{} columns but {} types",
columns.len(),
types.len()
)));
}
for ty in &types {
if !ty.is_basic() {
return Err(SparkplugError::DataSetShape(format!(
"column type {ty:?} is not a basic scalar type"
)));
}
}
for (i, row) in rows.iter().enumerate() {
if row.len() != columns.len() {
return Err(SparkplugError::DataSetShape(format!(
"row {i} has {} cells but there are {} columns",
row.len(),
columns.len()
)));
}
for (col, (cell, col_ty)) in row.iter().zip(&types).enumerate() {
if let Some(cell_ty) = cell.datatype()
&& cell_ty != *col_ty
{
return Err(SparkplugError::DataSetShape(format!(
"row {i} column {col}: cell type {cell_ty:?} does not match column type {col_ty:?}"
)));
}
}
}
Ok(Self {
columns,
types,
rows,
})
}
#[must_use]
pub fn num_of_columns(&self) -> u64 {
self.columns.len() as u64
}
#[must_use]
pub fn columns(&self) -> &[String] {
&self.columns
}
#[must_use]
pub fn types(&self) -> &[DataType] {
&self.types
}
#[must_use]
pub fn rows(&self) -> &[Vec<DataSetValue>] {
&self.rows
}
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct Template {
pub version: Option<String>,
pub template_ref: Option<String>,
pub is_definition: bool,
pub metrics: Vec<Metric>,
pub parameters: Vec<Parameter>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Parameter {
pub name: String,
pub datatype: DataType,
pub value: Option<ParameterValue>,
}