grafana-plugin-sdk 0.4.2

SDK for building Grafana backend plugins.
Documentation
//! Contains the `Field` struct, which holds actual data in the form of Arrow arrays, as well as column-specific metadata.
use std::{
    collections::{BTreeMap, HashMap},
    iter::FromIterator,
};

use arrow2::{
    array::Array,
    datatypes::{DataType, Field as ArrowField, TimeUnit},
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_with::skip_serializing_none;

use crate::data::{
    error,
    field_type::{FieldType, IntoFieldType},
    ConfFloat64,
};

/// A typed column within a [`Frame`][crate::data::Frame].
///
/// The underlying data for this field can be read using the [`Field::values`] method,
/// and updated using the [`Field::set_values`] and [`Field::set_values_opt`] methods.
#[derive(Debug)]
pub struct Field {
    /// The name of this field.
    ///
    /// Fields within a [`Frame`][crate::data::Frame] are not required to have unique names, but
    /// the combination of `name` and `labels` should be unique within a frame
    /// to ensure proper behaviour in all situations.
    pub name: String,
    /// An optional set of key-value pairs that, combined with the name, should uniquely identify a field within a [`Frame`][crate::data::Frame].
    pub labels: BTreeMap<String, String>,
    /// Optional display configuration used by Grafana.
    pub config: Option<FieldConfig>,

    /// The actual values of this field.
    ///
    /// The types of values contained within the `Array` MUST match the
    /// type information in `type_info` at all times. The various `into_field`-like
    /// functions and the `set_values` methods should ensure this.
    pub(crate) values: Box<dyn Array>,
    /// Type information for this field, as understood by Grafana.
    pub(crate) type_info: TypeInfo,
}

impl Field {
    pub(crate) fn to_arrow_field(&self) -> Result<ArrowField, serde_json::Error> {
        let metadata = match (self.labels.is_empty(), self.config.as_ref()) {
            (true, None) => Default::default(),
            (false, None) => [("labels".to_string(), serde_json::to_string(&self.labels)?)]
                .into_iter()
                .collect(),
            (false, Some(c)) => [("config".to_string(), serde_json::to_string(&c)?)]
                .into_iter()
                .collect(),
            (true, Some(c)) => [
                ("labels".to_string(), serde_json::to_string(&self.labels)?),
                ("config".to_string(), serde_json::to_string(&c)?),
            ]
            .into_iter()
            .collect(),
        };
        Ok(ArrowField {
            name: self.name.clone(),
            data_type: self.type_info.frame.into(),
            is_nullable: self.type_info.nullable.unwrap_or_default(),
            metadata,
        })
    }

    /// Return a new field with the given name.
    ///
    /// # Example
    ///
    /// ```rust
    /// use grafana_plugin_sdk::prelude::*;
    ///
    /// let field = ["a", "b", "c"]
    ///     .into_field("x")
    ///     .with_name("other name");
    /// assert_eq!(&field.name, "other name");
    /// ```
    #[must_use]
    pub fn with_name(mut self, name: impl Into<String>) -> Self {
        self.name = name.into();
        self
    }

    /// Return a new field with the given labels.
    ///
    /// # Example
    ///
    /// ```rust
    /// use std::collections::BTreeMap;
    /// use grafana_plugin_sdk::prelude::*;
    ///
    /// let mut labels = BTreeMap::default();
    /// labels.insert("some".to_string(), "value".to_string());
    /// let field = ["a", "b", "c"]
    ///     .into_field("x")
    ///     .with_labels(labels);
    /// assert_eq!(field.labels["some"], "value");
    /// ```
    #[must_use]
    pub fn with_labels(mut self, labels: BTreeMap<String, String>) -> Self {
        self.labels = labels;
        self
    }

    /// Return a new field with the given config.
    ///
    /// # Example
    ///
    /// ```rust
    /// use grafana_plugin_sdk::{data::FieldConfig, prelude::*};
    ///
    /// let mut config = FieldConfig::default();
    /// config.display_name_from_ds = Some("X".to_string());
    /// let field = ["a", "b", "c"]
    ///     .into_field("x")
    ///     .with_config(config);
    /// assert_eq!(&field.config.unwrap().display_name_from_ds.unwrap(), "X");
    /// ```
    #[must_use]
    pub fn with_config(mut self, config: impl Into<Option<FieldConfig>>) -> Self {
        self.config = config.into();
        self
    }

    /// Get the values of this field as a [`&dyn Array`].
    pub fn values(&self) -> &dyn Array {
        // This nightly clippy lint creates spurious suggestions for `&dyn Trait` return
        // types. This can be uncommented when https://github.com/rust-lang/rust-clippy/pull/9126
        // is released.
        #[allow(unknown_lints)]
        #[allow(clippy::explicit_auto_deref)]
        &*self.values
    }

    /// Set the values of this field using an iterator of values.
    ///
    /// # Errors
    ///
    /// Returns an [`Error::DataTypeMismatch`][error::Error::DataTypeMismatch] if the types of the new data
    /// do not match the types of the existing data.
    ///
    /// ```rust
    /// use arrow2::array::Utf8Array;
    /// use grafana_plugin_sdk::prelude::*;
    ///
    /// let mut field = ["a", "b", "c"]
    ///     .into_field("x");
    /// assert!(field.set_values(["d", "e", "f", "g"]).is_ok());
    /// assert_eq!(
    ///     field
    ///         .values()
    ///         .as_any()
    ///         .downcast_ref::<Utf8Array<i32>>()
    ///         .unwrap()
    ///         .iter()
    ///         .collect::<Vec<_>>(),
    ///     vec![Some("d"), Some("e"), Some("f"), Some("g")],
    /// );
    ///
    /// assert!(field.set_values([1u32, 2, 3]).is_err());
    /// ```
    pub fn set_values<T, U, V>(&mut self, values: T) -> Result<(), crate::data::error::Error>
    where
        T: IntoIterator<Item = U>,
        U: IntoFieldType<ElementType = V>,
        V: FieldType,
        V::Array: Array + FromIterator<Option<V>> + 'static,
    {
        let new_data_type: DataType = U::TYPE_INFO_TYPE.into();
        if self.values.data_type() != &new_data_type {
            return Err(crate::data::error::Error::DataTypeMismatch {
                existing: self.values.data_type().clone(),
                new: new_data_type,
                field: self.name.clone(),
            });
        }
        self.values = Box::new(V::convert_arrow_array(
            values
                .into_iter()
                .map(U::into_field_type)
                .collect::<V::Array>(),
            new_data_type,
        ));
        self.type_info.nullable = Some(false);
        Ok(())
    }

    /// Set the values of this field using an iterator of optional values.
    ///
    /// # Errors
    ///
    /// Returns an [`Error::DataTypeMismatch`][error::Error::DataTypeMismatch] if the types of the new data
    /// do not match the types of the existing data.
    ///
    /// ```rust
    /// use arrow2::array::Utf8Array;
    /// use grafana_plugin_sdk::prelude::*;
    ///
    /// let mut field = ["a", "b", "c"]
    ///     .into_field("x");
    /// assert!(field.set_values_opt([Some("d"), Some("e"), None, None]).is_ok());
    /// assert_eq!(
    ///     field
    ///         .values()
    ///         .as_any()
    ///         .downcast_ref::<Utf8Array<i32>>()
    ///         .unwrap()
    ///         .iter()
    ///         .collect::<Vec<_>>(),
    ///     vec![Some("d"), Some("e"), None, None],
    /// );
    ///
    /// assert!(field.set_values([Some(1u32), Some(2), None]).is_err());
    /// ```
    pub fn set_values_opt<T, U, V>(&mut self, values: T) -> Result<(), crate::data::error::Error>
    where
        T: IntoIterator<Item = Option<U>>,
        U: IntoFieldType<ElementType = V>,
        V: FieldType,
        V::Array: Array + FromIterator<Option<V>> + 'static,
    {
        let new_data_type: DataType = U::TYPE_INFO_TYPE.into();
        if self.values.data_type() != &new_data_type {
            return Err(crate::data::error::Error::DataTypeMismatch {
                existing: self.values.data_type().clone(),
                new: new_data_type,
                field: self.name.clone(),
            });
        }
        self.values = Box::new(V::convert_arrow_array(
            values
                .into_iter()
                .map(|x| x.and_then(U::into_field_type))
                .collect::<V::Array>(),
            new_data_type,
        ));
        self.type_info.nullable = Some(true);
        Ok(())
    }

    /// Set the values of this field using an [`Array`].
    ///
    /// # Errors
    ///
    /// Returns an [`Error::DataTypeMismatch`][error::Error::DataTypeMismatch] if the types of the new data
    /// do not match the types of the existing data.
    ///
    /// ```rust
    /// use arrow2::array::{PrimitiveArray, Utf8Array};
    /// use grafana_plugin_sdk::prelude::*;
    ///
    /// let mut field = ["a", "b", "c"]
    ///     .into_field("x");
    /// let new_values = Utf8Array::<i32>::from(["d", "e", "f"].map(Some));
    /// assert!(field.set_values_array(new_values).is_ok());
    /// assert_eq!(
    ///     field
    ///         .values()
    ///         .as_any()
    ///         .downcast_ref::<Utf8Array<i32>>()
    ///         .unwrap()
    ///         .iter()
    ///         .collect::<Vec<_>>(),
    ///     vec![Some("d"), Some("e"), Some("f")],
    /// );
    ///
    /// let bad_values = PrimitiveArray::<u32>::from([1, 2, 3].map(Some));
    /// assert!(field.set_values_array(bad_values).is_err());
    /// ```
    pub fn set_values_array<T>(&mut self, values: T) -> Result<(), crate::data::error::Error>
    where
        T: Array + 'static,
    {
        if self.values.data_type() != values.data_type() {
            return Err(crate::data::error::Error::DataTypeMismatch {
                existing: self.values.data_type().clone(),
                new: values.data_type().clone(),
                field: self.name.clone(),
            });
        }
        self.values = Box::new(values);
        Ok(())
    }
}

impl PartialEq for Field {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name
            && self.labels == other.labels
            && self.config == other.config
            && self.type_info == other.type_info
            && arrow2::array::equal(&*self.values, &*other.values)
    }
}

// Traits for creating a `Field` from various array-like things:
// iterators of both values and optional values, and arrays themselves.
// These need to be separate traits because otherwise the impls would conflict,
// as e.g. `Array` implements `IntoIterator`.

/// Indicates that a [`Field`] can be created from this type.
pub trait IntoField {
    /// Create a [`Field`] from `self`.
    ///
    /// The type of the `Field` will depend on the values in `self`.
    fn into_field(self, name: impl Into<String>) -> Field;
}

impl<T, U, V> IntoField for T
where
    T: IntoIterator<Item = U>,
    U: IntoFieldType<ElementType = V>,
    V: FieldType,
    V::Array: Array + FromIterator<Option<V>> + 'static,
{
    fn into_field(self, name: impl Into<String>) -> Field {
        Field {
            name: name.into(),
            labels: Default::default(),
            config: None,
            type_info: TypeInfo {
                frame: U::TYPE_INFO_TYPE,
                nullable: Some(false),
            },
            values: Box::new(V::convert_arrow_array(
                self.into_iter()
                    .map(U::into_field_type)
                    .collect::<V::Array>(),
                U::TYPE_INFO_TYPE.into(),
            )),
        }
    }
}

/// Indicates that a [`Field`] of optional values can be created from this type.
pub trait IntoOptField {
    /// Create a [`Field`] from `self`, with `None` values marked as null.
    fn into_opt_field(self, name: impl Into<String>) -> Field;
}

impl<T, U, V> IntoOptField for T
where
    T: IntoIterator<Item = Option<U>>,
    U: IntoFieldType<ElementType = V>,
    V: FieldType,
    V::Array: Array + FromIterator<Option<V>> + 'static,
{
    fn into_opt_field(self, name: impl Into<String>) -> Field {
        Field {
            name: name.into(),
            labels: Default::default(),
            config: None,
            type_info: TypeInfo {
                frame: U::TYPE_INFO_TYPE,
                nullable: Some(true),
            },
            values: Box::new(V::convert_arrow_array(
                self.into_iter()
                    .map(|x| x.and_then(U::into_field_type))
                    .collect::<V::Array>(),
                U::TYPE_INFO_TYPE.into(),
            )),
        }
    }
}

/// Helper trait for creating a [`Field`] from an [`Array`][arrow2::array::Array].
pub trait ArrayIntoField {
    /// Create a `Field` using `self` as the values.
    ///
    /// # Errors
    ///
    /// This returns an error if the values are not valid field types.
    fn try_into_field(self, name: impl Into<String>) -> Result<Field, error::Error>;
}

impl<T> ArrayIntoField for T
where
    T: Array + 'static,
{
    fn try_into_field(self, name: impl Into<String>) -> Result<Field, error::Error> {
        Ok(Field {
            name: name.into(),
            labels: Default::default(),
            config: None,
            type_info: TypeInfo {
                frame: self.data_type().try_into()?,
                nullable: Some(true),
            },
            values: Box::new(self),
        })
    }
}

/// The type information for a [`Frame`][crate::data::Frame] as understood by Grafana.
#[skip_serializing_none]
#[derive(Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TypeInfo {
    /// The type, as understood by Grafana.
    pub(crate) frame: TypeInfoType,
    /// Is this type nullable?
    #[serde(default)]
    pub(crate) nullable: Option<bool>,
}

impl TypeInfo {
    /// Get the corresponding [`SimpleType`].
    #[must_use]
    pub const fn simple_type(&self) -> SimpleType {
        self.frame.simple_type()
    }
}

/// Valid types understood by Grafana.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum TypeInfoType {
    /// An 8 bit signed integer.
    Int8,
    /// A 16 bit signed integer.
    Int16,
    /// A 32 bit signed integer.
    Int32,
    /// A 64 bit signed integer.
    Int64,
    /// An 8 bit unsigned integer.
    UInt8,
    /// A 16 bit unsigned integer.
    UInt16,
    /// A 32 bit unsigned integer.
    UInt32,
    /// A 64 bit unsigned integer.
    UInt64,
    /// A 32 bit float.
    Float32,
    /// A 64 bit float.
    Float64,
    /// A string.
    String,
    /// A boolean.
    Bool,
    /// A timestamp, in UTC.
    // For compatibility with the Go SDK.
    #[serde(rename = "time.Time")]
    Time,
}

impl TryFrom<&DataType> for TypeInfoType {
    type Error = error::Error;
    fn try_from(other: &DataType) -> Result<Self, Self::Error> {
        Ok(match other {
            DataType::Int8 => Self::Int8,
            DataType::Int16 => Self::Int16,
            DataType::Int32 => Self::Int32,
            DataType::Int64 => Self::Int64,
            DataType::UInt8 => Self::UInt8,
            DataType::UInt16 => Self::UInt16,
            DataType::UInt32 => Self::UInt32,
            DataType::UInt64 => Self::UInt64,
            DataType::Float32 => Self::Float32,
            DataType::Float64 => Self::Float64,
            DataType::Utf8 => Self::String,
            DataType::Boolean => Self::Bool,
            DataType::Timestamp(..) => Self::Time,
            // TODO - handle time correctly.
            other => return Err(error::Error::UnsupportedArrowDataType(other.clone())),
        })
    }
}

impl From<TypeInfoType> for DataType {
    fn from(other: TypeInfoType) -> Self {
        match other {
            TypeInfoType::Int8 => Self::Int8,
            TypeInfoType::Int16 => Self::Int16,
            TypeInfoType::Int32 => Self::Int32,
            TypeInfoType::Int64 => Self::Int64,
            TypeInfoType::UInt8 => Self::UInt8,
            TypeInfoType::UInt16 => Self::UInt16,
            TypeInfoType::UInt32 => Self::UInt32,
            TypeInfoType::UInt64 => Self::UInt64,
            TypeInfoType::Float32 => Self::Float32,
            TypeInfoType::Float64 => Self::Float64,
            TypeInfoType::String => Self::Utf8,
            TypeInfoType::Bool => Self::Boolean,
            TypeInfoType::Time => Self::Timestamp(TimeUnit::Nanosecond, None),
        }
    }
}

impl TypeInfoType {
    #[must_use]
    pub(crate) const fn simple_type(&self) -> SimpleType {
        match self {
            Self::Int8
            | Self::Int16
            | Self::Int32
            | Self::Int64
            | Self::UInt8
            | Self::UInt16
            | Self::UInt32
            | Self::UInt64
            | Self::Float32
            | Self::Float64 => SimpleType::Number,
            Self::String => SimpleType::String,
            Self::Bool => SimpleType::Boolean,
            Self::Time => SimpleType::Time,
        }
    }
}

/// The 'simple' type of this data.
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum SimpleType {
    /// A number.
    Number,
    /// A boolean.
    Boolean,
    /// A string.
    String,
    /// A timestamp.
    Time,
}

/// The display properties for a [`Field`].
///
/// These are used by the Grafana frontend to modify how the field is displayed.
///
/// Note that this struct, like most structs in this crate, is marked `#[non_exhaustive]` and
/// therefore cannot be constructed using a struct expression. Instead, create a default
/// value using `FieldConfig::default()` and modify any fields necessary.
// This struct needs to match the frontend component defined in:
// https://github.com/grafana/grafana/blob/master/packages/grafana-data/src/types/dataFrame.ts#L23
// All properties are optional should be omitted from JSON when empty or not set.
#[skip_serializing_none]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct FieldConfig {
    /// Overrides Grafana default naming.
    ///
    /// This should not be used from a datasource.
    #[serde(default)]
    pub display_name: Option<String>,

    /// Overrides Grafana default naming in a better way that allows users to further override it easily.
    ///
    /// This should be used instead of `display_name` when used from a datasource.
    #[serde(default, rename = "displayNameFromDS")]
    pub display_name_from_ds: Option<String>,

    /// An explicit path to the field in the datasource.
    ///
    /// When the frame meta includes a path, this will default to `${frame.meta.path}/${field.name}.
    ///
    /// When defined, this value can be used as an identifier within the datasource scope, and
    /// may be used as an identifier to update values in a subsequent request.
    #[serde(default)]
    pub path: Option<String>,

    /// Human readable field metadata.
    #[serde(default)]
    pub description: Option<String>,

    /// Indicates that the datasource knows how to update this value.
    #[serde(default)]
    pub writeable: Option<bool>,

    /// Indicates if the field's data can be filtered by additional calls.
    #[serde(default)]
    pub filterable: Option<bool>,

    /// The string to display to represent this field's unit, such as "Requests/sec".
    #[serde(default)]
    pub unit: Option<String>,
    /// The number of decimal places to display.
    #[serde(default)]
    pub decimals: Option<u16>,
    /// The minimum value of fields in the column.
    ///
    /// When present the frontend can skip the calculation.
    #[serde(default)]
    pub min: Option<f64>,
    /// The maximum value of fields in the column.
    ///
    /// When present the frontend can skip the calculation.
    #[serde(default)]
    pub max: Option<f64>,

    /// Mappings from input value to display string.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub mappings: Vec<ValueMapping>,
    /// Mappings from numeric values to states.
    #[serde(default)]
    pub thresholds: Option<ThresholdsConfig>,

    /// Links to use when clicking on a result.
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub links: Vec<DataLink>,

    /// An alternative string to use when no value is present.
    ///
    /// The default is an empty string.
    #[serde(default)]
    pub no_value: Option<String>,

    /// Panel-specific values.
    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
    pub custom: HashMap<String, Value>,
}

/// Special values that can be mapped to new text and colour values.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged, rename_all = "camelCase")]
#[non_exhaustive]
pub enum SpecialValueMatch {
    /// Match `true`.
    True,
    /// Match `false`.
    False,
    /// Match `null`.
    Null,
    /// Match `NaN`.
    NaN,
    /// Match `null` and `NaN`.
    #[serde(rename = "null+nan")]
    NullAndNaN,
    /// Match empty values.
    Empty,
}

/// Allows input values to be mapped to text and colour.
#[skip_serializing_none]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", content = "options", rename_all = "camelCase")]
#[non_exhaustive]
pub enum ValueMapping {
    /// Map strings to new strings directly.
    ValueMapper(HashMap<String, ValueMappingResult>),
    /// Map special values to new values.
    SpecialValueMapper {
        /// The input value to match.
        #[serde(rename = "match")]
        match_: SpecialValueMatch,
        /// The result to be shown instead of the matched value.
        result: ValueMappingResult,
    },
    /// Map ranges of floats to new values.
    RangeValueMapper {
        /// The minimum value to match.
        from: ConfFloat64,
        /// The maximum value to match.
        to: ConfFloat64,
        /// The result to be shown instead of the matched values.
        result: ValueMappingResult,
    },
}

/// A new value to be displayed when a [`ValueMapping`] matches an input value.
#[skip_serializing_none]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ValueMappingResult {
    /// The text to display.
    #[serde(default)]
    pub text: Option<String>,
    /// The colour to use when displaying the value.
    #[serde(default)]
    pub color: Option<String>,
    /// Used for ordering in the UI.
    #[serde(default)]
    pub index: Option<isize>,
}

/// Configuration for thresholds for a field.
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct ThresholdsConfig {
    /// How thresholds should be evaluated.
    pub mode: ThresholdsMode,
    /// The threshold steps.
    pub steps: Vec<Threshold>,
}

/// A single step on the threshold list.
#[skip_serializing_none]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Threshold {
    /// The upper bound of this threshold.
    pub value: Option<ConfFloat64>,
    /// The colour to use for this threshold.
    pub color: Option<String>,
    /// The state of the alert.
    pub state: Option<String>,
}

/// How thresholds should be evaluated.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub enum ThresholdsMode {
    /// Pick thresholds based on the absolute value.
    ///
    /// This is the default value.
    Absolute,
    /// Thresholds should be treated as relative to the min/max.
    Percentage,
}

impl Default for ThresholdsMode {
    fn default() -> Self {
        Self::Absolute
    }
}

/// Links to use when clicking on a result.
#[skip_serializing_none]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct DataLink {
    /// The title text to display.
    pub title: Option<String>,
    /// Is the target blank?
    pub target_blank: Option<bool>,
    /// The URL to link to.
    pub url: Option<String>,
}

#[cfg(test)]
mod tests {

    use std::time::UNIX_EPOCH;

    use chrono::prelude::*;
    use paste::paste;

    use super::*;

    macro_rules! test_create_field_from_type {
        ($t:ty) => {
            paste! {
                #[test]
                #[allow(non_snake_case)]
                fn [< create_field_from_ $t >]() {
                    let field = [<$t>::default()].into_field("x".to_string());
                    assert_eq!(field.name, "x");
                    assert_eq!(field.values.len(), 1)
                }

                #[test]
                #[allow(non_snake_case)]
                fn [< create_field_from_opt_ $t >]() {
                    let field = [Some(<$t>::default()), None].into_opt_field("x".to_string());
                    assert_eq!(field.name, "x");
                    assert_eq!(field.values.len(), 2)
                }

                #[test]
                #[allow(non_snake_case)]
                fn [< create_field_from_iter_ $t >]() {
                    let field = std::iter::once(<$t>::default()).into_field("x".to_string());
                    assert_eq!(field.name, "x");
                    assert_eq!(field.values.len(), 1)
                }

                #[test]
                #[allow(non_snake_case)]
                fn [< create_field_from_opt_iter_ $t >]() {
                    let field = std::iter::once(Some(<$t>::default())).into_opt_field("x".to_string());
                    assert_eq!(field.name, "x");
                    assert_eq!(field.values.len(), 1)
                }

                #[test]
                #[allow(non_snake_case)]
                fn [< create_field_from_array_ $t >]() {
                    let array = <$t as FieldType>::Array::from_slice([<$t>::default()]);
                    let field = array.try_into_field("x".to_string()).unwrap();
                    assert_eq!(field.name, "x");
                    assert_eq!(field.values.len(), 1)
                }
            }
        };
        ($t:ty, $e: expr) => {
            paste! {
                #[test]
                #[allow(non_snake_case)]
                fn [< create_field_from_ $t >]() {
                    let field = [$e].into_field("x".to_string());
                    assert_eq!(field.name, "x");
                    assert_eq!(field.values.len(), 1)
                }

                #[test]
                #[allow(non_snake_case)]
                fn [< create_field_from_opt_ $t >]() {
                    let field = [Some($e), None].into_opt_field("x".to_string());
                    assert_eq!(field.name, "x");
                    assert_eq!(field.values.len(), 2)
                }

                #[test]
                #[allow(non_snake_case)]
                fn [< create_field_from_iter_ $t >]() {
                    let field = std::iter::once($e).into_field("x".to_string());
                    assert_eq!(field.name, "x");
                    assert_eq!(field.values.len(), 1)
                }

                #[test]
                #[allow(non_snake_case)]
                fn [< create_field_from_opt_iter_ $t >]() {
                    let field = std::iter::once(Some($e)).into_opt_field("x".to_string());
                    assert_eq!(field.name, "x");
                    assert_eq!(field.values.len(), 1)
                }
            }
        };
    }

    test_create_field_from_type!(bool);
    test_create_field_from_type!(i8);
    test_create_field_from_type!(i16);
    test_create_field_from_type!(i32);
    test_create_field_from_type!(i64);
    test_create_field_from_type!(u8);
    test_create_field_from_type!(u16);
    test_create_field_from_type!(u32);
    test_create_field_from_type!(u64);
    test_create_field_from_type!(f32);
    test_create_field_from_type!(f64);
    test_create_field_from_type!(String);
    test_create_field_from_type!(str, "");
    test_create_field_from_type!(SystemTime, UNIX_EPOCH);
    test_create_field_from_type!(
        NaiveDateTime,
        NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0)
    );
    test_create_field_from_type!(NaiveDate, NaiveDate::from_ymd(1970, 1, 1));
    test_create_field_from_type!(DateTime, Utc.ymd(1970, 1, 1).and_hms(0, 0, 0));
    test_create_field_from_type!(Date, Utc.ymd(1970, 1, 1));

    #[test]
    fn set_values_from_iter_primitive() {
        let mut field = vec![1u32, 2, 3].into_field("yhat".to_string());
        assert!(field.set_values([4u32, 5, 6]).is_ok());
        assert!(field.set_values([4u64, 5, 6]).is_err());
    }

    // #[test]
    // fn set_values_from_array_primitive() {
    //     let array = PrimitiveArray::<u32>::from_slice([1u32, 2, 3]);
    //     let mut field = array.try_into_field("yhat".to_string()).unwrap();
    //     let array = PrimitiveArray::<u32>::from_slice([4u32, 5, 6]);
    //     assert!(field.set_values(array).is_ok());
    //     let array = PrimitiveArray::<u64>::from_slice([4u64, 5, 6]);
    //     assert!(field.set_values(array).is_err());
    // }
}