use std::fmt;
#[cfg(feature = "converter")]
use qubit_datatype::DataConversionError;
#[cfg(feature = "converter")]
use qubit_datatype::DataConversionErrorKind;
use qubit_datatype::DataType;
use crate::ValueMissingReason;
#[must_use]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ValueMissing {
reason: ValueMissingReason,
source_type: Option<DataType>,
target_type: Option<DataType>,
source_index: Option<usize>,
#[cfg(feature = "converter")]
conversion_error: Option<DataConversionError>,
}
impl ValueMissing {
#[must_use = "the unset scalar descriptor should be inspected or returned"]
#[inline(always)]
pub const fn unset_scalar(source: DataType, target: DataType) -> Self {
Self::storage(ValueMissingReason::UnsetScalar, source, target)
}
#[must_use = "the unset collection descriptor should be inspected or returned"]
#[inline(always)]
pub const fn unset_collection(source: DataType, target: DataType) -> Self {
Self::storage(ValueMissingReason::UnsetCollection, source, target)
}
#[must_use = "the empty collection descriptor should be inspected or returned"]
#[inline(always)]
pub const fn empty_collection(source: DataType, target: DataType) -> Self {
Self::storage(ValueMissingReason::EmptyCollection, source, target)
}
#[must_use = "the storage descriptor should be retained"]
#[inline(always)]
const fn storage(reason: ValueMissingReason, source: DataType, target: DataType) -> Self {
Self {
reason,
source_type: Some(source),
target_type: Some(target),
source_index: None,
#[cfg(feature = "converter")]
conversion_error: None,
}
}
#[cfg(feature = "converter")]
#[must_use = "the conversion descriptor should be retained"]
#[inline]
pub(crate) fn from_conversion(error: DataConversionError, source_index: Option<usize>) -> Self {
Self {
reason: if error.kind() == DataConversionErrorKind::EmptyCollection {
ValueMissingReason::EmptyCollection
} else {
ValueMissingReason::Conversion
},
source_type: error.from_type(),
target_type: Some(error.to_type()),
source_index,
conversion_error: Some(error),
}
}
#[cfg(feature = "converter")]
#[must_use = "the enriched descriptor should replace the original"]
#[inline(always)]
pub(crate) fn with_storage(mut self, source: DataType, reason: ValueMissingReason) -> Self {
self.source_type = Some(source);
self.reason = reason;
self
}
#[cfg(feature = "converter")]
#[must_use = "the indexed descriptor should replace the original"]
#[inline]
pub(crate) fn with_first_index(mut self) -> Self {
if self.reason == ValueMissingReason::Conversion && self.source_index.is_none() {
self.source_index = Some(0);
}
self
}
#[must_use]
#[inline(always)]
pub const fn reason(&self) -> ValueMissingReason {
self.reason
}
#[must_use]
#[inline(always)]
pub const fn source_type(&self) -> Option<DataType> {
self.source_type
}
#[must_use]
#[inline(always)]
pub const fn target_type(&self) -> Option<DataType> {
self.target_type
}
#[must_use]
#[inline(always)]
pub const fn source_index(&self) -> Option<usize> {
self.source_index
}
#[cfg(feature = "converter")]
#[must_use]
#[inline(always)]
pub const fn conversion_error(&self) -> Option<&DataConversionError> {
self.conversion_error.as_ref()
}
#[must_use]
#[inline(always)]
pub const fn is_unset(&self) -> bool {
matches!(
self.reason,
ValueMissingReason::UnsetScalar | ValueMissingReason::UnsetCollection
)
}
#[must_use]
#[inline(always)]
pub const fn is_empty_collection(&self) -> bool {
matches!(self.reason, ValueMissingReason::EmptyCollection)
}
#[must_use]
#[inline]
pub const fn is_conversion(&self) -> bool {
if matches!(self.reason, ValueMissingReason::Conversion) {
return true;
}
#[cfg(feature = "converter")]
{
self.conversion_error.is_some()
}
#[cfg(not(feature = "converter"))]
{
false
}
}
#[must_use]
#[inline(always)]
pub const fn is_defaultable_for_strict_read(&self) -> bool {
self.is_unset()
}
#[must_use]
#[inline]
pub const fn is_defaultable_for_conversion(&self) -> bool {
self.is_unset() || (matches!(self.reason, ValueMissingReason::Conversion) && self.source_index.is_none())
}
}
impl fmt::Display for ValueMissing {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"{:?}: source {:?}, target {:?}",
self.reason, self.source_type, self.target_type
)?;
if let Some(index) = self.source_index {
write!(formatter, ", collection index {index}")?;
}
Ok(())
}
}
impl std::error::Error for ValueMissing {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
#[cfg(feature = "converter")]
{
self.conversion_error
.as_ref()
.map(|error| error as &dyn std::error::Error)
}
#[cfg(not(feature = "converter"))]
{
None
}
}
}