qubit-datatype 0.12.0

Runtime data type descriptors and conversion utilities for Rust
Documentation
// =============================================================================
//    Copyright (c) 2025 - 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================
//! # Data Type Definitions (Language Layer)
//!
//! Provides cross-module reusable common data type enum `DataType` and type
//! mapping `DataTypeOf`.

use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;
use std::str::FromStr;

use serde::Deserialize;
use serde::Serialize;

use super::DataTypeCategory;
use super::data_type_parse_error::DataTypeParseError;

/// Universal data type enumeration for cross-module type representation
///
/// Defines the fixed runtime data types supported by the system.
/// This enum provides a unified way to represent and work with different data
/// types across various modules and components.
///
/// `DataType` serves as a bridge between Rust's type system and runtime type
/// information, enabling dynamic type handling, serialization, validation,
/// and other type-aware operations.
///
/// # Features
///
/// - **Stable Coverage**: Supports fixed-width Rust primitives and common
///   third-party types while intentionally omitting `isize` and `usize`
/// - **String Representation**: Each variant has a consistent string
///   representation
/// - **Serialization Support**: Implements `Serialize` and `Deserialize` for
///   JSON/YAML support
/// - **Type Mapping**: Works with `DataTypeOf` trait for compile-time type
///   mapping
///
/// # Use Cases
///
/// - **Dynamic Type Handling**: Runtime type checking and conversion
/// - **Serialization/Deserialization**: Type-aware data format conversion
/// - **Validation Systems**: Type-based input validation
/// - **Generic Programming**: Type-safe generic operations
/// - **API Documentation**: Automatic type information generation
///
/// # Examples
///
/// ## Basic Usage
///
/// ```rust
/// use qubit_datatype::DataType;
///
/// let data_type = DataType::Int32;
/// assert_eq!(data_type.to_string(), "int32");
/// assert_eq!(data_type.as_str(), "int32");
/// ```
///
/// ## Type Checking
///
/// ```rust
/// use qubit_datatype::DataType;
///
/// assert!(DataType::Int32.is_numeric());
/// assert!(!DataType::String.is_numeric());
/// ```
///
/// ## Serialization
///
/// ```rust
/// use qubit_datatype::DataType;
/// use serde_json;
///
/// let data_type = DataType::Float64;
/// let json = serde_json::to_string(&data_type).unwrap();
/// assert_eq!(json, "\"float64\"");
///
/// let deserialized: DataType = serde_json::from_str(&json).unwrap();
/// assert_eq!(deserialized, DataType::Float64);
/// ```
///
/// This enum intentionally defines the closed runtime type vocabulary used by
/// exhaustive downstream mappings. Adding a variant is a deliberate breaking
/// change.
#[must_use]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DataType {
    /// Boolean type
    Bool,
    /// Character type
    Char,
    /// 8-bit signed integer
    Int8,
    /// 16-bit signed integer
    Int16,
    /// 32-bit signed integer
    Int32,
    /// 64-bit signed integer
    Int64,
    /// 128-bit signed integer
    Int128,
    /// 8-bit unsigned integer
    UInt8,
    /// 16-bit unsigned integer
    UInt16,
    /// 32-bit unsigned integer
    UInt32,
    /// 64-bit unsigned integer
    UInt64,
    /// 128-bit unsigned integer
    UInt128,
    /// 32-bit floating point number
    Float32,
    /// 64-bit floating point number
    Float64,
    /// String type
    String,
    /// Date type (NaiveDate)
    Date,
    /// Time type (NaiveTime)
    Time,
    /// DateTime type (NaiveDateTime)
    DateTime,
    /// UTC time point (equivalent to Java Instant) (`DateTime<Utc>`)
    Instant,
    /// Big integer type (BigInt)
    BigInteger,
    /// Big decimal type (BigDecimal)
    BigDecimal,
    /// Duration type (std::time::Duration)
    Duration,
    /// URL type (url::Url)
    Url,
    /// String map type (HashMap<String, String>)
    StringMap,
    /// JSON value type (serde_json::Value)
    Json,
}

impl DataType {
    /// All data type variants in their stable declaration order.
    pub const ALL: &'static [DataType] = &[
        DataType::Bool,
        DataType::Char,
        DataType::Int8,
        DataType::Int16,
        DataType::Int32,
        DataType::Int64,
        DataType::Int128,
        DataType::UInt8,
        DataType::UInt16,
        DataType::UInt32,
        DataType::UInt64,
        DataType::UInt128,
        DataType::Float32,
        DataType::Float64,
        DataType::String,
        DataType::Date,
        DataType::Time,
        DataType::DateTime,
        DataType::Instant,
        DataType::BigInteger,
        DataType::BigDecimal,
        DataType::Duration,
        DataType::Url,
        DataType::StringMap,
        DataType::Json,
    ];

    /// Returns the broad semantic category of this data type.
    ///
    /// Categories describe the stable runtime vocabulary and do not depend on
    /// enabled Cargo features.
    ///
    /// # Returns
    ///
    /// The semantic category for this data type.
    #[must_use = "the data type category is returned"]
    #[inline(always)]
    pub const fn category(self) -> DataTypeCategory {
        match self {
            DataType::Bool => DataTypeCategory::Boolean,
            DataType::Char => DataTypeCategory::Character,
            DataType::Int8 | DataType::Int16 | DataType::Int32 | DataType::Int64 | DataType::Int128 => {
                DataTypeCategory::SignedInteger
            }
            DataType::UInt8 | DataType::UInt16 | DataType::UInt32 | DataType::UInt64 | DataType::UInt128 => {
                DataTypeCategory::UnsignedInteger
            }
            DataType::Float32 | DataType::Float64 => DataTypeCategory::FloatingPoint,
            DataType::String => DataTypeCategory::Text,
            DataType::Date | DataType::Time | DataType::DateTime | DataType::Instant => DataTypeCategory::Temporal,
            DataType::BigInteger | DataType::BigDecimal => DataTypeCategory::BigNumber,
            DataType::Duration => DataTypeCategory::Duration,
            DataType::Url => DataTypeCategory::Locator,
            DataType::StringMap | DataType::Json => DataTypeCategory::Structured,
        }
    }

    /// Returns the stable lowercase name of this data type.
    ///
    /// # Returns
    ///
    /// The stable lowercase serialization and display name.
    #[must_use]
    #[inline(always)]
    pub const fn as_str(self) -> &'static str {
        match self {
            DataType::Bool => "bool",
            DataType::Char => "char",
            DataType::Int8 => "int8",
            DataType::Int16 => "int16",
            DataType::Int32 => "int32",
            DataType::Int64 => "int64",
            DataType::Int128 => "int128",
            DataType::UInt8 => "uint8",
            DataType::UInt16 => "uint16",
            DataType::UInt32 => "uint32",
            DataType::UInt64 => "uint64",
            DataType::UInt128 => "uint128",
            DataType::Float32 => "float32",
            DataType::Float64 => "float64",
            DataType::String => "string",
            DataType::Date => "date",
            DataType::Time => "time",
            DataType::DateTime => "datetime",
            DataType::Instant => "instant",
            DataType::BigInteger => "biginteger",
            DataType::BigDecimal => "bigdecimal",
            DataType::Duration => "duration",
            DataType::Url => "url",
            DataType::StringMap => "stringmap",
            DataType::Json => "json",
        }
    }

    /// Tests whether this type belongs to the numeric family.
    ///
    /// # Returns
    ///
    /// `true` for fixed-width integers, primitive floats, and big numbers.
    #[must_use]
    #[inline(always)]
    pub const fn is_numeric(self) -> bool {
        self.is_integer() || self.is_float() || self.is_big_number()
    }

    /// Tests whether this type is a fixed-width integer.
    ///
    /// # Returns
    ///
    /// `true` for signed or unsigned fixed-width integer variants.
    #[must_use]
    #[inline(always)]
    pub const fn is_integer(self) -> bool {
        self.is_signed_integer() || self.is_unsigned_integer()
    }

    /// Tests whether this type is a signed integer.
    ///
    /// # Returns
    ///
    /// `true` for `Int8` through `Int128`.
    #[must_use]
    #[inline(always)]
    pub const fn is_signed_integer(self) -> bool {
        matches!(
            self,
            DataType::Int8 | DataType::Int16 | DataType::Int32 | DataType::Int64 | DataType::Int128
        )
    }

    /// Tests whether this type is an unsigned integer.
    ///
    /// # Returns
    ///
    /// `true` for `UInt8` through `UInt128`.
    #[must_use]
    #[inline(always)]
    pub const fn is_unsigned_integer(self) -> bool {
        matches!(
            self,
            DataType::UInt8 | DataType::UInt16 | DataType::UInt32 | DataType::UInt64 | DataType::UInt128
        )
    }

    /// Tests whether this type is a primitive floating-point type.
    ///
    /// # Returns
    ///
    /// `true` for [`Self::Float32`] or [`Self::Float64`].
    #[must_use]
    #[inline(always)]
    pub const fn is_float(self) -> bool {
        matches!(self, DataType::Float32 | DataType::Float64)
    }

    /// Tests whether this type is an arbitrary-precision number.
    ///
    /// # Returns
    ///
    /// `true` for [`Self::BigInteger`] or [`Self::BigDecimal`].
    #[must_use]
    #[inline(always)]
    pub const fn is_big_number(self) -> bool {
        matches!(self, DataType::BigInteger | DataType::BigDecimal)
    }
}

impl Display for DataType {
    /// Writes the stable lowercase type name to the formatter.
    #[inline(always)]
    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
        formatter.write_str(self.as_str())
    }
}

impl FromStr for DataType {
    type Err = DataTypeParseError;

    /// Parses a type name case-insensitively using the stable vocabulary.
    fn from_str(input: &str) -> Result<Self, Self::Err> {
        Self::ALL
            .iter()
            .copied()
            .find(|data_type| input.eq_ignore_ascii_case(data_type.as_str()))
            .ok_or_else(|| DataTypeParseError::new(input))
    }
}

impl From<DataType> for &'static str {
    /// Converts a type descriptor to its stable lowercase name.
    #[inline(always)]
    fn from(value: DataType) -> Self {
        value.as_str()
    }
}

impl From<&DataType> for &'static str {
    /// Converts a borrowed type descriptor to its stable lowercase name.
    #[inline(always)]
    fn from(value: &DataType) -> Self {
        value.as_str()
    }
}