use qubit_datatype::DataType;
#[cfg(feature = "converter")]
use qubit_datatype::{
DataConversionOptions,
DataConversionTarget,
};
use std::fmt;
use crate::value_error::ValueResult;
use crate::{
IntoValueDefault,
ValueError,
};
use super::value_ref::ValueRef;
macro_rules! define_value_enum {
(
;
$(
(
[$($cfg:meta),*],
$variant:ident,
$type:ty,
$data_type:expr,
$materialization:ident,
$json_class:ident,
$number_projection:ident,
$value_doc:literal,
$multi_doc:literal
)
),+ $(,)?
) => {
#[derive(Debug, Clone)]
pub(crate) enum ValueRepr {
Unset(
/// Declared data type retained while the value is unset.
DataType,
),
$(
$(#[$cfg])*
#[doc = $value_doc]
$variant(
#[doc = concat!("Stored ", $value_doc, " payload.")]
value_storage_type!($variant, $type),
),
)+
}
};
}
for_each_value_type!(define_value_enum);
#[must_use]
#[derive(Clone)]
pub struct Value {
pub(crate) repr: ValueRepr,
}
impl fmt::Debug for Value {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.view().fmt(formatter)
}
}
macro_rules! impl_value_constructors {
(
;
$(
(
[$($cfg:meta),*],
$variant:ident,
$type:ty,
$data_type:expr,
$materialization:ident,
$json_class:ident,
$number_projection:ident,
$value_doc:literal,
$multi_doc:literal
)
),+ $(,)?
) => {
impl Value {
#[allow(non_snake_case)]
#[inline(always)]
pub const fn Unset(data_type: DataType) -> Self {
Self::new_unset(data_type)
}
#[inline(always)]
pub const fn new_unset(data_type: DataType) -> Self {
Self { repr: ValueRepr::Unset(data_type) }
}
$(
$(#[$cfg])*
#[allow(non_snake_case)]
#[doc = concat!("Creates a ", $value_doc, ".")]
#[inline(always)]
pub fn $variant(value: $type) -> Self {
Self { repr: ValueRepr::$variant(value_storage_new!($variant, value)) }
}
)+
}
};
}
for_each_value_type!(impl_value_constructors);
impl Value {
#[inline(always)]
pub fn view(&self) -> ValueRef<'_> {
match &self.repr {
ValueRepr::Unset(data_type) => ValueRef::Unset(*data_type),
ValueRepr::Bool(value) => ValueRef::Bool(*value),
ValueRepr::Char(value) => ValueRef::Char(*value),
ValueRepr::Int8(value) => ValueRef::Int8(*value),
ValueRepr::Int16(value) => ValueRef::Int16(*value),
ValueRepr::Int32(value) => ValueRef::Int32(*value),
ValueRepr::Int64(value) => ValueRef::Int64(*value),
ValueRepr::Int128(value) => ValueRef::Int128(*value),
ValueRepr::UInt8(value) => ValueRef::UInt8(*value),
ValueRepr::UInt16(value) => ValueRef::UInt16(*value),
ValueRepr::UInt32(value) => ValueRef::UInt32(*value),
ValueRepr::UInt64(value) => ValueRef::UInt64(*value),
ValueRepr::UInt128(value) => ValueRef::UInt128(*value),
ValueRepr::Float32(value) => ValueRef::Float32(*value),
ValueRepr::Float64(value) => ValueRef::Float64(*value),
#[cfg(feature = "big-integer")]
ValueRepr::BigInteger(value) => ValueRef::BigInteger(value),
#[cfg(feature = "big-decimal")]
ValueRepr::BigDecimal(value) => ValueRef::BigDecimal(value),
ValueRepr::String(value) => ValueRef::String(value),
#[cfg(feature = "chrono")]
ValueRepr::Date(value) => ValueRef::Date(value),
#[cfg(feature = "chrono")]
ValueRepr::Time(value) => ValueRef::Time(value),
#[cfg(feature = "chrono")]
ValueRepr::DateTime(value) => ValueRef::DateTime(value),
#[cfg(feature = "chrono")]
ValueRepr::Instant(value) => ValueRef::Instant(value),
ValueRepr::Duration(value) => ValueRef::Duration(value),
#[cfg(feature = "url")]
ValueRepr::Url(value) => ValueRef::Url(value.as_ref()),
ValueRepr::StringMap(value) => ValueRef::StringMap(value),
#[cfg(feature = "json")]
ValueRepr::Json(value) => ValueRef::Json(value),
}
}
}
macro_rules! value_data_type_match {
($value:expr; $(([$($cfg:meta),*], $variant:ident, $type:ty, $data_type:expr, $materialization:ident, $json_class:ident, $number_projection:ident, $value_doc:literal, $multi_doc:literal)),+ $(,)?) => {
match &$value.repr {
ValueRepr::Unset(data_type) => *data_type,
$($(#[$cfg])* ValueRepr::$variant(_) => $data_type,)+
}
};
}
impl Value {
#[inline(always)]
pub fn new<T>(value: T) -> Self
where
T: Into<Self>,
{
value.into()
}
#[inline(always)]
pub fn get<T>(&self) -> ValueResult<T>
where
for<'a> T: TryFrom<&'a Self, Error = ValueError>,
{
T::try_from(self)
}
#[inline]
pub fn get_or<T>(&self, default: impl IntoValueDefault<T>) -> ValueResult<T>
where
for<'a> T: TryFrom<&'a Self, Error = ValueError>,
{
match self.get() {
Err(ValueError::Missing(missing)) if missing.is_unset() => {
Ok(default.into_value_default())
}
result => result,
}
}
#[inline]
pub fn get_or_else<T, F>(&self, default: F) -> ValueResult<T>
where
for<'a> T: TryFrom<&'a Self, Error = ValueError>,
F: FnOnce() -> T,
{
match self.get() {
Err(ValueError::Missing(missing)) if missing.is_unset() => {
Ok(default())
}
result => result,
}
}
#[inline(always)]
#[cfg(feature = "converter")]
pub fn to<T>(&self) -> ValueResult<T>
where
T: DataConversionTarget,
{
self.to_with(DataConversionOptions::default_ref())
}
#[inline]
#[cfg(feature = "converter")]
pub fn to_or<T>(&self, default: impl IntoValueDefault<T>) -> ValueResult<T>
where
T: DataConversionTarget,
{
match self.to() {
Err(ValueError::Missing(missing))
if missing.is_defaultable_for_conversion() =>
{
Ok(default.into_value_default())
}
result => result,
}
}
#[inline]
#[cfg(feature = "converter")]
pub fn to_or_else<T, F>(&self, default: F) -> ValueResult<T>
where
T: DataConversionTarget,
F: FnOnce() -> T,
{
match self.to() {
Err(ValueError::Missing(missing))
if missing.is_defaultable_for_conversion() =>
{
Ok(default())
}
result => result,
}
}
#[inline(always)]
#[cfg(feature = "converter")]
pub fn to_with<T>(&self, options: &DataConversionOptions) -> ValueResult<T>
where
T: DataConversionTarget,
{
super::value_converters::convert_with_data_converter_with(self, options)
}
#[inline]
#[cfg(feature = "converter")]
pub fn to_or_with<T>(
&self,
default: impl IntoValueDefault<T>,
options: &DataConversionOptions,
) -> ValueResult<T>
where
T: DataConversionTarget,
{
match self.to_with(options) {
Err(ValueError::Missing(missing))
if missing.is_defaultable_for_conversion() =>
{
Ok(default.into_value_default())
}
result => result,
}
}
#[inline]
#[cfg(feature = "converter")]
pub fn to_or_else_with<T, F>(
&self,
default: F,
options: &DataConversionOptions,
) -> ValueResult<T>
where
T: DataConversionTarget,
F: FnOnce() -> T,
{
match self.to_with(options) {
Err(ValueError::Missing(missing))
if missing.is_defaultable_for_conversion() =>
{
Ok(default())
}
result => result,
}
}
#[inline(always)]
pub fn set<T>(&mut self, value: T)
where
T: Into<Self>,
{
*self = value.into();
}
#[inline(always)]
pub fn data_type(&self) -> DataType {
for_each_value_type!(value_data_type_match, self)
}
#[inline(always)]
#[must_use]
pub fn is_unset(&self) -> bool {
matches!(self.repr, ValueRepr::Unset(_))
}
#[inline(always)]
#[must_use]
pub fn is_numeric(&self) -> bool {
!self.is_unset() && self.data_type().is_numeric()
}
#[inline(always)]
pub fn unset(&mut self) {
*self = Value::new_unset(self.data_type());
}
#[inline]
pub fn set_type(&mut self, data_type: DataType) {
if self.data_type() != data_type {
*self = Value::new_unset(data_type);
}
}
}