connect-stream-types 0.1.1

Type definitions for streaming types used internally by Nominal Connect
Documentation
pub mod sample_table;

use std::slice::IterMut;

pub use sample_table::*;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::de::Error as _;

/// A value taken from a channel.
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
#[serde(untagged)]
pub enum Value {
    String(String),
    Double(f64),
    Integer(i64),
}

impl From<String> for Value {
    fn from(value: String) -> Self {
        Value::String(value)
    }
}

impl From<&str> for Value {
    fn from(value: &str) -> Self {
        Value::String(value.to_owned())
    }
}

impl From<f64> for Value {
    fn from(value: f64) -> Self {
        Value::Double(value)
    }
}

impl From<i64> for Value {
    fn from(value: i64) -> Self {
        Value::Integer(value)
    }
}

impl Default for Value {
    fn default() -> Value {
        Value::Double(0.0)
    }
}

impl Value {
    pub fn into_series(self) -> ValueSeries {
        self.into()
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ValueRef<'value> {
    String(&'value str),
    Double(&'value f64),
    Integer(&'value i64),
}

pub enum ValueRefMut<'value> {
    String(&'value mut String),
    Double(&'value mut f64),
    Integer(&'value mut i64),
}

impl PartialEq<Value> for ValueRef<'_> {
    fn eq(&self, other: &Value) -> bool {
        match (*self, other) {
            (ValueRef::String(s), Value::String(s2)) => s == s2,
            (ValueRef::Double(d), Value::Double(d2)) => d == d2,
            (ValueRef::Integer(i), Value::Integer(i2)) => i == i2,
            _ => false,
        }
    }
}

/// A series of values. Mirrors the `nominal-streaming::PointsType` enum. We
/// avoid simply re-using that type to avoid coupling the app to the api too
/// closely.
#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum ValueSeries {
    String(Vec<String>),
    Double(Vec<f64>),
    Integer(Vec<i64>),
}

impl ValueSeries {
    pub fn get(&self, index: usize) -> Option<ValueRef<'_>> {
        match self {
            ValueSeries::String(values) => values.get(index).map(|s| ValueRef::String(s.as_str())),
            ValueSeries::Double(values) => values.get(index).map(ValueRef::Double),
            ValueSeries::Integer(values) => values.get(index).map(ValueRef::Integer),
        }
    }

    fn transpose_inner<T: Into<Value>>(values: Vec<T>) -> Vec<ValueSeries> {
        values.into_iter().map(|v| v.into().into_series()).collect()
    }

    pub fn transpose(self) -> Vec<ValueSeries> {
        match self {
            Self::String(v) => Self::transpose_inner(v),
            Self::Double(v) => Self::transpose_inner(v),
            Self::Integer(v) => Self::transpose_inner(v),
        }
    }
}

#[derive(Debug)]
pub struct ValueSeriesIter<'values> {
    values: &'values ValueSeries,
    index: usize,
}

#[derive(Debug)]
pub enum ValueSeriesIterMut<'values> {
    String(IterMut<'values, String>),
    Double(IterMut<'values, f64>),
    Integer(IterMut<'values, i64>),
}

impl<'values> ValueSeriesIter<'values> {
    fn new(values: &'values ValueSeries) -> Self {
        Self { values, index: 0 }
    }
}

impl<'values> ValueSeriesIterMut<'values> {
    fn new(values: &'values mut ValueSeries) -> Self {
        match values {
            ValueSeries::String(values) => ValueSeriesIterMut::String(values.iter_mut()),
            ValueSeries::Double(values) => ValueSeriesIterMut::Double(values.iter_mut()),
            ValueSeries::Integer(values) => ValueSeriesIterMut::Integer(values.iter_mut()),
        }
    }
}

impl<'values> Iterator for ValueSeriesIter<'values> {
    type Item = ValueRef<'values>;

    fn next(&mut self) -> Option<Self::Item> {
        self.values.get(self.index).inspect(|_| {
            self.index = self.index.saturating_add(1);
        })
    }
}

impl<'values> Iterator for ValueSeriesIterMut<'values> {
    type Item = ValueRefMut<'values>;

    fn next(&mut self) -> Option<Self::Item> {
        match self {
            ValueSeriesIterMut::String(values) => values.next().map(ValueRefMut::String),
            ValueSeriesIterMut::Double(values) => values.next().map(ValueRefMut::Double),
            ValueSeriesIterMut::Integer(values) => values.next().map(ValueRefMut::Integer),
        }
    }
}

impl<'values> IntoIterator for &'values ValueSeries {
    type Item = ValueRef<'values>;
    type IntoIter = ValueSeriesIter<'values>;

    fn into_iter(self) -> Self::IntoIter {
        ValueSeriesIter::new(self)
    }
}

impl From<Vec<String>> for ValueSeries {
    fn from(values: Vec<String>) -> Self {
        ValueSeries::String(values)
    }
}

impl From<Vec<&str>> for ValueSeries {
    fn from(values: Vec<&str>) -> Self {
        ValueSeries::String(values.into_iter().map(|s| s.to_owned()).collect())
    }
}

impl From<Vec<f64>> for ValueSeries {
    fn from(values: Vec<f64>) -> Self {
        ValueSeries::Double(values)
    }
}

impl From<Vec<i64>> for ValueSeries {
    fn from(values: Vec<i64>) -> Self {
        ValueSeries::Integer(values)
    }
}

impl From<Value> for ValueSeries {
    fn from(value: Value) -> Self {
        match value {
            Value::String(string) => ValueSeries::String(vec![string]),
            Value::Double(double) => ValueSeries::Double(vec![double]),
            Value::Integer(int) => ValueSeries::Integer(vec![int]),
        }
    }
}

impl Default for ValueSeries {
    fn default() -> ValueSeries {
        ValueSeries::Double(Vec::new())
    }
}

impl ValueSeries {
    pub fn iter(&self) -> ValueSeriesIter<'_> {
        ValueSeriesIter::new(self)
    }

    pub fn iter_mut(&mut self) -> ValueSeriesIterMut<'_> {
        ValueSeriesIterMut::new(self)
    }

    pub const fn len(&self) -> usize {
        match self {
            ValueSeries::String(values) => values.len(),
            ValueSeries::Double(values) => values.len(),
            ValueSeries::Integer(values) => values.len(),
        }
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Returns an iterator over numeric values cast to `f64`.
    /// Returns `None` for `String` variants.
    pub fn iter_numeric_as_f64(&self) -> Option<Box<dyn Iterator<Item = f64> + '_>> {
        match self {
            ValueSeries::Double(vals) => Some(Box::new(vals.iter().copied())),
            ValueSeries::Integer(vals) => Some(Box::new(vals.iter().map(|&v| v as f64))),
            ValueSeries::String(_) => None,
        }
    }

    // Resizes down to the selected size
    pub fn truncate(&mut self, size: usize) {
        match self {
            ValueSeries::String(values) => values.truncate(size),
            ValueSeries::Double(values) => values.truncate(size),
            ValueSeries::Integer(values) => values.truncate(size),
        }
    }
}

/// Custom deserializer that accepts usize from either a number or a string with underscores.
/// For example: `5000` or `"5_000"` both deserialize to `5000`.
fn usize_with_underscores<'de, D: Deserializer<'de>>(deserializer: D) -> Result<usize, D::Error> {
    #[derive(Deserialize)]
    #[serde(untagged)]
    enum UsizeOrString {
        Usize(usize),
        String(String),
    }

    let value: UsizeOrString = serde::Deserialize::deserialize(deserializer)?;
    match value {
        UsizeOrString::Usize(value) => Ok(value),
        UsizeOrString::String(value) => value
            .replace('_', "")
            .parse::<usize>()
            .map_err(D::Error::custom),
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum BufferCapacity {
    Points(#[serde(deserialize_with = "usize_with_underscores")] usize),
    Duration {
        unit: TimeUnit,
        #[serde(deserialize_with = "usize_with_underscores")]
        value: usize,
    },
}

impl Default for BufferCapacity {
    fn default() -> Self {
        Self::Points(5000)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TimeUnit {
    #[serde(alias = "milliseconds")]
    #[serde(alias = "millis")]
    #[serde(alias = "ms")]
    Milliseconds,
    #[serde(alias = "seconds")]
    #[serde(alias = "sec")]
    #[serde(alias = "s")]
    Seconds,
    #[serde(alias = "minutes")]
    #[serde(alias = "min")]
    #[serde(alias = "m")]
    Minutes,
    #[serde(alias = "hours")]
    #[serde(alias = "hr")]
    #[serde(alias = "h")]
    Hours,
}

impl TimeUnit {
    /// Converts this time unit and a value to a `core::time::Duration`.
    pub fn to_duration(&self, value: usize) -> core::time::Duration {
        match self {
            TimeUnit::Milliseconds => core::time::Duration::from_millis(value as u64),
            TimeUnit::Seconds => core::time::Duration::from_secs(value as u64),
            TimeUnit::Minutes => {
                core::time::Duration::from_secs(60_u64.saturating_mul(value as u64))
            }
            TimeUnit::Hours => {
                core::time::Duration::from_secs(3600_u64.saturating_mul(value as u64))
            }
        }
    }
}

impl BufferCapacity {
    /// The amount of items that the buffer should accommodate.
    ///
    /// For the point count, this is the number of data points that can be stored in the buffer.
    /// For time-based units, this is the number of "units" that can be stored (the actual point
    /// count varies).
    pub fn value(&self) -> usize {
        match self {
            BufferCapacity::Points(points) => *points,
            BufferCapacity::Duration { value, unit: _ } => *value,
        }
    }
}