use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt;
#[cfg(feature = "json")]
use std::hash::Hash;
#[cfg(feature = "json")]
use std::hash::Hasher;
use std::time::Duration;
#[cfg(feature = "big-decimal")]
use bigdecimal::BigDecimal;
#[cfg(feature = "chrono")]
use chrono::DateTime;
#[cfg(feature = "chrono")]
use chrono::NaiveDate;
#[cfg(feature = "chrono")]
use chrono::NaiveDateTime;
#[cfg(feature = "chrono")]
use chrono::NaiveTime;
#[cfg(feature = "chrono")]
use chrono::Utc;
#[cfg(feature = "big-integer")]
use num_bigint::BigInt;
#[cfg(feature = "json")]
use qubit_budget::MeasuredBudgetError;
#[cfg(feature = "json")]
use qubit_budget::ResourceQuantity;
#[cfg(feature = "json")]
use qubit_budget::json::JsonValueBudget;
#[cfg(feature = "converter")]
use qubit_datatype::ConversionLimits;
#[cfg(feature = "converter")]
use qubit_datatype::ConversionPolicy;
#[cfg(feature = "converter")]
use qubit_datatype::ConversionSession;
#[cfg(all(feature = "converter", feature = "json"))]
use qubit_datatype::DataConversionError;
#[cfg(feature = "converter")]
use qubit_datatype::DataConversionTarget;
#[cfg(all(feature = "converter", feature = "json"))]
use qubit_datatype::DataFormat;
use qubit_datatype::DataType;
#[cfg(all(feature = "converter", feature = "json"))]
use qubit_datatype::InvalidValueReason;
use qubit_datatype::NumberRef;
use qubit_datatype::NumericComparisonPolicy;
#[cfg(all(feature = "converter", feature = "json"))]
use qubit_json::encode::JsonSerializationErrorKind;
#[cfg(all(feature = "converter", feature = "json"))]
use qubit_json::value::JsonValueEncoder;
#[cfg(all(feature = "converter", feature = "json"))]
use serde::Deserialize;
#[cfg(all(feature = "converter", feature = "json"))]
use serde::Serialize;
#[cfg(all(feature = "converter", feature = "json"))]
use serde::de::DeserializeOwned;
#[cfg(feature = "url")]
use url::Url;
use super::internal::ValueRepr;
use super::value_ref::ValueRef;
use crate::IntoValueDefault;
use crate::NumericComparisonError;
use crate::ValueError;
use crate::ValueMissing;
#[cfg(feature = "json")]
use crate::identity::hash_json;
#[cfg(feature = "json")]
use crate::identity::preflight_json;
#[cfg(feature = "json")]
use crate::value::value_identity::hash_value_payload_with_json_budget;
use crate::value_error::ValueResult;
#[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
$(, $_wire:tt)*
)
),+ $(,)?
) => {
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) }
}
$(
#[doc = concat!("Creates a ", $value_doc, ".")]
$(#[$cfg])*
#[allow(non_snake_case)]
#[inline(always)]
pub fn $variant(value: $type) -> Self {
Self { repr: ValueRepr::$variant(value_storage_new!($variant, value)) }
}
)+
}
};
}
macro_rules! owned_view_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 $(, $_wire:tt)*)),+ $(,)?) => {
match &$value.repr {
ValueRepr::Unset(data_type) => ValueRef::Unset(*data_type),
$($(#[$cfg])* ValueRepr::$variant(value) => ValueRef::$variant(
value_view_payload!($variant, $number_projection, value_storage_ref!($variant, value))
),)+
}
};
}
for_each_value_type!(impl_value_constructors);
impl Value {
#[must_use = "the borrowed strict value result should be handled"]
#[inline(always)]
pub fn get_ref<'a, T: ?Sized>(&'a self) -> ValueResult<&'a T>
where
&'a T: TryFrom<&'a Self, Error = ValueError>,
{
<&'a T>::try_from(self)
}
#[cfg(feature = "json")]
pub fn hash_with_json_budget<H, R, Q>(
&self,
state: &mut H,
budget: &mut JsonValueBudget<R, Q>,
) -> Result<(), MeasuredBudgetError<R, Q>>
where
H: Hasher,
R: Clone,
Q: ResourceQuantity,
{
match &self.repr {
ValueRepr::Json(value) => {
let mut transaction = budget.transaction();
preflight_json(value, &mut transaction)?;
std::mem::discriminant(&self.repr).hash(state);
hash_json(value, state);
transaction.commit()
}
_ => {
std::mem::discriminant(&self.repr).hash(state);
hash_value_payload_with_json_budget(&self.repr, state, budget)
}
}
}
#[must_use = "the borrowed value view should be used"]
#[inline(always)]
pub fn view(&self) -> ValueRef<'_> {
for_each_value_type!(owned_view_match, self)
}
}
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 $(, $_wire:tt)*)),+ $(,)?) => {
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()
}
#[must_use = "the strict value read result should be handled"]
#[inline(always)]
pub fn get<T>(&self) -> ValueResult<T>
where
for<'a> T: TryFrom<&'a Self, Error = ValueError>,
{
T::try_from(self)
}
#[must_use = "the strict value read result should be handled"]
#[inline(always)]
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_defaultable_for_strict_read() => {
Ok(default.into_value_default())
}
result => result,
}
}
#[must_use = "the strict value read result should be handled"]
#[inline(always)]
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_defaultable_for_strict_read() => Ok(default()),
result => result,
}
}
#[inline(always)]
#[cfg(feature = "converter")]
pub fn to<T>(&self) -> ValueResult<T>
where
T: DataConversionTarget,
{
self.to_with(ConversionPolicy::default_ref(), ConversionLimits::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, policy: &ConversionPolicy, limits: &ConversionLimits) -> ValueResult<T>
where
T: DataConversionTarget,
{
super::value_converters::convert_with_data_converter_with(self, policy, limits)
}
#[inline(always)]
#[cfg(feature = "converter")]
pub fn to_in<T>(&self, session: &mut ConversionSession<'_>) -> ValueResult<T>
where
T: DataConversionTarget,
{
super::value_converters::convert_with_data_converter_in(self, session)
}
#[inline]
#[cfg(feature = "converter")]
pub fn to_or_with<T>(
&self,
default: impl IntoValueDefault<T>,
policy: &ConversionPolicy,
limits: &ConversionLimits,
) -> ValueResult<T>
where
T: DataConversionTarget,
{
match self.to_with(policy, limits) {
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,
policy: &ConversionPolicy,
limits: &ConversionLimits,
) -> ValueResult<T>
where
T: DataConversionTarget,
F: FnOnce() -> T,
{
match self.to_with(policy, limits) {
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();
}
#[must_use = "the runtime data type should be used"]
#[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(always)]
pub fn set_type(&mut self, data_type: DataType) {
if self.data_type() != data_type {
*self = Value::new_unset(data_type);
}
}
}
#[cfg(all(feature = "converter", feature = "json"))]
impl Value {
#[inline(always)]
pub fn to_json_value(&self) -> ValueResult<serde_json::Value> {
self.to_json_value_with(ConversionPolicy::default_ref(), ConversionLimits::default_ref())
}
#[inline(always)]
pub fn to_json_value_with(
&self,
policy: &ConversionPolicy,
limits: &ConversionLimits,
) -> ValueResult<serde_json::Value> {
crate::json::value_to_json_value_with(self, policy, limits)
}
}
macro_rules! impl_get_value {
($(#[$attr:meta])* copy: $method:ident, $variant:ident, $type:ty, $data_type:expr) => {
$(#[$attr])*
#[doc = ""]
#[doc = "# Errors"]
#[doc = ""]
#[doc = "Returns [`ValueError::Missing`] when the value is unset with"]
#[doc = "the requested type, or [`ValueError::TypeMismatch`] when the"]
#[doc = "stored data type differs."]
#[must_use = "the strict value read result should be handled"]
#[inline(always)]
pub fn $method(&self) -> ValueResult<$type> {
match &self.repr {
ValueRepr::$variant(v) => Ok(*v),
ValueRepr::Unset(dt) if *dt == $data_type => {
Err(ValueError::Missing($crate::ValueMissing::unset_scalar(*dt, *dt)))
}
ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
expected: $data_type,
actual: *dt,
}),
_ => Err(ValueError::TypeMismatch {
expected: $data_type,
actual: self.data_type(),
}),
}
}
};
($(#[$attr:meta])* ref: $method:ident, $variant:ident, $ret_type:ty, $data_type:expr, $conversion:expr) => {
$(#[$attr])*
#[doc = ""]
#[doc = "# Errors"]
#[doc = ""]
#[doc = "Returns [`ValueError::Missing`] when the value is unset with"]
#[doc = "the requested type, or [`ValueError::TypeMismatch`] when the"]
#[doc = "stored data type differs."]
#[must_use = "the strict value read result should be handled"]
#[inline(always)]
pub fn $method(&self) -> ValueResult<$ret_type> {
match &self.repr {
ValueRepr::$variant(v) => {
let conv_fn: fn(&_) -> $ret_type = $conversion;
Ok(conv_fn(v))
},
ValueRepr::Unset(dt) if *dt == $data_type => {
Err(ValueError::Missing($crate::ValueMissing::unset_scalar(*dt, *dt)))
}
ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
expected: $data_type,
actual: *dt,
}),
_ => Err(ValueError::TypeMismatch {
expected: $data_type,
actual: self.data_type(),
}),
}
}
};
}
impl Value {
#[inline(always)]
#[cfg(feature = "json")]
pub fn from_json_value(json: serde_json::Value) -> Self {
Value::Json(json)
}
#[cfg(all(feature = "converter", feature = "json"))]
pub fn from_serializable<T: ?Sized + Serialize>(value: &T) -> ValueResult<Self> {
let json = JsonValueEncoder::new().encode(value).map_err(|error| {
let reason = match error.kind() {
JsonSerializationErrorKind::NonFiniteFloat => InvalidValueReason::NonFinite,
JsonSerializationErrorKind::IntegerOutOfRange { .. } => InvalidValueReason::OutOfRange,
_ => InvalidValueReason::Serialization {
format: DataFormat::Json,
},
};
ValueError::from(DataConversionError::invalid(DataType::Json, DataType::Json, reason))
})?;
Ok(Value::Json(json))
}
impl_get_value! {
copy: get_bool, Bool, bool, DataType::Bool
}
impl_get_value! {
copy: get_char, Char, char, DataType::Char
}
impl_get_value! {
copy: get_int8, Int8, i8, DataType::Int8
}
impl_get_value! {
copy: get_int16, Int16, i16, DataType::Int16
}
impl_get_value! {
copy: get_int32, Int32, i32, DataType::Int32
}
impl_get_value! {
copy: get_int64, Int64, i64, DataType::Int64
}
impl_get_value! {
copy: get_int128, Int128, i128, DataType::Int128
}
impl_get_value! {
copy: get_uint8, UInt8, u8, DataType::UInt8
}
impl_get_value! {
copy: get_uint16, UInt16, u16, DataType::UInt16
}
impl_get_value! {
copy: get_uint32, UInt32, u32, DataType::UInt32
}
impl_get_value! {
copy: get_uint64, UInt64, u64, DataType::UInt64
}
impl_get_value! {
copy: get_uint128, UInt128, u128, DataType::UInt128
}
impl_get_value! {
copy: get_float32, Float32, f32, DataType::Float32
}
impl_get_value! {
copy: get_float64, Float64, f64, DataType::Float64
}
impl_get_value! {
ref: get_string, String, &str, DataType::String, |s: &String| s.as_str()
}
#[cfg(feature = "chrono")]
impl_get_value! {
copy: get_date, Date, NaiveDate, DataType::Date
}
#[cfg(feature = "chrono")]
impl_get_value! {
copy: get_time, Time, NaiveTime, DataType::Time
}
#[cfg(feature = "chrono")]
impl_get_value! {
copy: get_datetime, DateTime, NaiveDateTime, DataType::DateTime
}
#[cfg(feature = "chrono")]
impl_get_value! {
copy: get_instant, Instant, DateTime<Utc>, DataType::Instant
}
#[cfg(feature = "big-integer")]
impl_get_value! {
ref: get_biginteger, BigInteger, BigInt, DataType::BigInteger, |v: &BigInt| v.clone()
}
#[cfg(feature = "big-decimal")]
impl_get_value! {
ref: get_bigdecimal, BigDecimal, BigDecimal, DataType::BigDecimal, |v: &BigDecimal| v.clone()
}
impl_get_value! {
copy: get_duration, Duration, Duration, DataType::Duration
}
#[cfg(feature = "url")]
impl_get_value! {
ref: get_url, Url, Url, DataType::Url, Url::clone
}
impl_get_value! {
ref: get_string_map, StringMap, HashMap<String, String>, DataType::StringMap,
|v: &HashMap<String, String>| v.clone()
}
#[cfg(feature = "json")]
impl_get_value! {
ref: get_json, Json, serde_json::Value, DataType::Json,
|v: &serde_json::Value| v.clone()
}
#[cfg(feature = "big-integer")]
#[must_use = "the strict value read result should be handled"]
#[inline(always)]
pub fn get_biginteger_ref(&self) -> ValueResult<&BigInt> {
match &self.repr {
ValueRepr::BigInteger(v) => Ok(v),
ValueRepr::Unset(dt) if *dt == DataType::BigInteger => {
Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
}
ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
expected: DataType::BigInteger,
actual: *dt,
}),
_ => Err(ValueError::TypeMismatch {
expected: DataType::BigInteger,
actual: self.data_type(),
}),
}
}
#[cfg(feature = "big-decimal")]
#[must_use = "the strict value read result should be handled"]
#[inline(always)]
pub fn get_bigdecimal_ref(&self) -> ValueResult<&BigDecimal> {
match &self.repr {
ValueRepr::BigDecimal(v) => Ok(v),
ValueRepr::Unset(dt) if *dt == DataType::BigDecimal => {
Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
}
ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
expected: DataType::BigDecimal,
actual: *dt,
}),
_ => Err(ValueError::TypeMismatch {
expected: DataType::BigDecimal,
actual: self.data_type(),
}),
}
}
#[cfg(feature = "url")]
#[must_use = "the strict value read result should be handled"]
#[inline(always)]
pub fn get_url_ref(&self) -> ValueResult<&Url> {
match &self.repr {
ValueRepr::Url(v) => Ok(v.as_ref()),
ValueRepr::Unset(dt) if *dt == DataType::Url => {
Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
}
ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
expected: DataType::Url,
actual: *dt,
}),
_ => Err(ValueError::TypeMismatch {
expected: DataType::Url,
actual: self.data_type(),
}),
}
}
#[must_use = "the strict value read result should be handled"]
#[inline(always)]
pub fn get_string_map_ref(&self) -> ValueResult<&HashMap<String, String>> {
match &self.repr {
ValueRepr::StringMap(v) => Ok(v),
ValueRepr::Unset(dt) if *dt == DataType::StringMap => {
Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
}
ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
expected: DataType::StringMap,
actual: *dt,
}),
_ => Err(ValueError::TypeMismatch {
expected: DataType::StringMap,
actual: self.data_type(),
}),
}
}
#[cfg(feature = "json")]
#[must_use = "the strict value read result should be handled"]
#[inline(always)]
pub fn get_json_ref(&self) -> ValueResult<&serde_json::Value> {
match &self.repr {
ValueRepr::Json(v) => Ok(v),
ValueRepr::Unset(dt) if *dt == DataType::Json => {
Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
}
ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
expected: DataType::Json,
actual: *dt,
}),
_ => Err(ValueError::TypeMismatch {
expected: DataType::Json,
actual: self.data_type(),
}),
}
}
#[cfg(all(feature = "converter", feature = "json"))]
pub fn deserialize_json<T: DeserializeOwned>(&self) -> ValueResult<T> {
match &self.repr {
ValueRepr::Json(v) => Deserialize::deserialize(v).map_err(|_| {
ValueError::from(DataConversionError::invalid(
DataType::Json,
DataType::Json,
InvalidValueReason::Deserialization {
format: DataFormat::Json,
},
))
}),
ValueRepr::Unset(dt) if *dt == DataType::Json => {
Err(ValueError::Missing(ValueMissing::unset_scalar(*dt, *dt)))
}
ValueRepr::Unset(dt) => Err(ValueError::TypeMismatch {
expected: DataType::Json,
actual: *dt,
}),
_ => Err(ValueError::TypeMismatch {
expected: DataType::Json,
actual: self.data_type(),
}),
}
}
}
macro_rules! project_number_ref {
(number_copy, $value:expr) => {
Some(NumberRef::from(*$value))
};
(number_ref, $value:expr) => {
Some(NumberRef::from($value))
};
(not_number, $value:expr) => {{
let _ = $value;
None
}};
}
macro_rules! value_number_ref_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 $(, $_wire:tt)*)),+ $(,)?) => {
match &$value.repr {
ValueRepr::Unset(_) => None,
$(
$(#[$cfg])*
ValueRepr::$variant(value) => {
project_number_ref!($number_projection, value)
}
)+
}
};
}
impl Value {
#[inline(always)]
#[must_use]
pub fn is_nan(&self) -> bool {
self.as_number_ref().is_some_and(|value| value.is_nan())
}
pub fn numeric_cmp(
&self,
other: &Self,
policy: NumericComparisonPolicy,
) -> Result<Ordering, NumericComparisonError> {
if let ValueRepr::Unset(declared) = &self.repr {
return Err(NumericComparisonError::LeftMissing { declared: *declared });
}
if let ValueRepr::Unset(declared) = &other.repr {
return Err(NumericComparisonError::RightMissing { declared: *declared });
}
let left = self
.as_number_ref()
.ok_or_else(|| NumericComparisonError::LeftNotNumeric {
actual: self.data_type(),
})?;
let right = other
.as_number_ref()
.ok_or_else(|| NumericComparisonError::RightNotNumeric {
actual: other.data_type(),
})?;
match (left.is_nan(), right.is_nan()) {
(true, true) => return Err(NumericComparisonError::BothNaN),
(true, false) => return Err(NumericComparisonError::LeftNaN),
(false, true) => return Err(NumericComparisonError::RightNaN),
(false, false) => {}
}
match left.compare(right, policy) {
Some(ordering) => Ok(ordering),
None => unreachable!("validated non-NaN numeric values must be orderable"),
}
}
#[must_use]
fn as_number_ref(&self) -> Option<NumberRef<'_>> {
for_each_value_type!(value_number_ref_match, self)
}
}