use std::{
fmt::Debug,
sync::{
atomic::{self, AtomicPtr},
Arc,
},
};
use serde::{
de::{DeserializeOwned, Error as _},
Deserialize, Deserializer, Serialize,
};
use crate::{Error, Value};
#[derive(Clone)]
pub enum ShouldBe<T> {
AndIs(T),
ButIsnt(WhyNot),
}
impl<T> ShouldBe<T> {
pub fn as_ref(&self) -> Option<&T> {
match self {
ShouldBe::AndIs(value) => Some(value),
ShouldBe::ButIsnt(_) => None,
}
}
pub fn as_ref_mut(&mut self) -> Option<&mut T> {
match self {
ShouldBe::AndIs(value) => Some(value),
ShouldBe::ButIsnt(_) => None,
}
}
pub fn as_ref_raw(&self) -> Option<&crate::Value> {
match self {
ShouldBe::AndIs(_) => None,
ShouldBe::ButIsnt(why_not) => why_not.as_ref_raw(),
}
}
pub fn as_err_msg(&self) -> Option<&str> {
match self {
ShouldBe::AndIs(_) => None,
ShouldBe::ButIsnt(why_not) => Some(why_not.as_msg()),
}
}
pub fn is(&self) -> bool {
matches!(self, ShouldBe::AndIs(_))
}
pub fn isnt(&self) -> bool {
matches!(self, ShouldBe::ButIsnt(_))
}
pub fn into_inner(self) -> Option<T> {
match self {
ShouldBe::AndIs(value) => Some(value),
ShouldBe::ButIsnt(_) => None,
}
}
pub fn take_err(&self) -> Option<Error> {
match self {
ShouldBe::AndIs(_) => None,
ShouldBe::ButIsnt(why_not) => why_not.take_err(),
}
}
}
impl<T> Debug for ShouldBe<T>
where
T: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ShouldBe::AndIs(value) => value.fmt(f),
ShouldBe::ButIsnt(why_not) => {
write!(f, "ShouldBe::ButIsnt({:?})", why_not)
}
}
}
}
impl<T> Default for ShouldBe<T>
where
T: Default,
{
fn default() -> Self {
ShouldBe::AndIs(T::default())
}
}
impl<T> From<T> for ShouldBe<T> {
fn from(value: T) -> Self {
ShouldBe::AndIs(value)
}
}
impl<T> From<ShouldBe<T>> for Option<T> {
fn from(should_be: ShouldBe<T>) -> Self {
should_be.into_inner()
}
}
impl<T> From<ShouldBe<T>> for Result<T, Error> {
fn from(should_be: ShouldBe<T>) -> Self {
match should_be {
ShouldBe::AndIs(value) => Ok(value),
ShouldBe::ButIsnt(why_not) => Err(why_not.into()),
}
}
}
impl<T> PartialEq for ShouldBe<T>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(ShouldBe::AndIs(a), ShouldBe::AndIs(b)) => a == b,
(ShouldBe::ButIsnt(a), ShouldBe::ButIsnt(b)) => a == b,
_ => false,
}
}
}
impl<T> Eq for ShouldBe<T> where T: Eq {}
impl<T> PartialOrd for ShouldBe<T>
where
T: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (self, other) {
(ShouldBe::AndIs(a), ShouldBe::AndIs(b)) => a.partial_cmp(b),
(ShouldBe::ButIsnt(a), ShouldBe::ButIsnt(b)) => a.partial_cmp(b),
(ShouldBe::AndIs(_), ShouldBe::ButIsnt(_)) => Some(std::cmp::Ordering::Greater),
(ShouldBe::ButIsnt(_), ShouldBe::AndIs(_)) => Some(std::cmp::Ordering::Less),
}
}
}
impl<T> Ord for ShouldBe<T>
where
T: Ord,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self, other) {
(ShouldBe::AndIs(a), ShouldBe::AndIs(b)) => a.cmp(b),
(ShouldBe::ButIsnt(a), ShouldBe::ButIsnt(b)) => a.cmp(b),
(ShouldBe::AndIs(_), ShouldBe::ButIsnt(_)) => std::cmp::Ordering::Greater,
(ShouldBe::ButIsnt(_), ShouldBe::AndIs(_)) => std::cmp::Ordering::Less,
}
}
}
impl<T> std::hash::Hash for ShouldBe<T>
where
T: std::hash::Hash,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
ShouldBe::AndIs(value) => value.hash(state),
ShouldBe::ButIsnt(why_not) => why_not.hash(state),
}
}
}
impl<T> Serialize for ShouldBe<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
ShouldBe::AndIs(value) => value.serialize(serializer),
ShouldBe::ButIsnt(why_not) => {
if let Some(raw_value) = why_not.as_ref_raw() {
raw_value.serialize(serializer)
} else {
Err(serde::ser::Error::custom(
"Cannot serialize `ShouldBe::ButIsnt` without a raw value",
))
}
}
}
}
}
impl<'de, T> Deserialize<'de> for ShouldBe<T>
where
T: DeserializeOwned,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
EXPECTING_SHOULD_BE.with(|cell| *cell.borrow_mut() = true);
match T::deserialize(deserializer) {
Ok(value) => Ok(ShouldBe::AndIs(value)),
Err(err) => {
if let Some((raw, err)) = take_why_not() {
Ok(ShouldBe::ButIsnt(WhyNot::new(Some(raw), err)))
} else {
let err = Error::custom(err);
Ok(ShouldBe::ButIsnt(WhyNot::new(None, err)))
}
}
}
}
}
#[derive(Clone)]
pub struct WhyNot(Arc<WhyNotImpl>);
struct WhyNotImpl {
raw: Option<crate::Value>,
err: AtomicPtr<Error>,
err_msg: String,
}
impl WhyNot {
pub fn new(raw: Option<crate::Value>, err: Error) -> Self {
let err_msg = err.to_string();
Self(Arc::new(WhyNotImpl {
raw,
err: AtomicPtr::new(Box::into_raw(Box::new(err))),
err_msg,
}))
}
fn take_err(&self) -> Option<Error> {
let ptr = self
.0
.err
.swap(std::ptr::null_mut(), atomic::Ordering::SeqCst);
if ptr.is_null() {
None
} else {
Some(
unsafe { *Box::from_raw(ptr) },
)
}
}
fn as_ref_raw(&self) -> Option<&crate::Value> {
self.0.raw.as_ref()
}
fn as_msg(&self) -> &str {
&self.0.err_msg
}
}
impl PartialEq for WhyNot {
fn eq(&self, other: &Self) -> bool {
self.as_ref_raw() == other.as_ref_raw() && self.as_msg() == other.as_msg()
}
}
impl Eq for WhyNot {}
impl PartialOrd for WhyNot {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for WhyNot {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.as_ref_raw().partial_cmp(&other.as_ref_raw()) {
Some(std::cmp::Ordering::Equal) | None => self.as_msg().cmp(other.as_msg()),
Some(ord) => ord,
}
}
}
impl std::hash::Hash for WhyNot {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.as_ref_raw().hash(state);
self.as_msg().hash(state);
}
}
impl From<WhyNot> for Error {
fn from(why_not: WhyNot) -> Self {
if let Some(err) = why_not.take_err() {
err
} else {
Error::custom(why_not.as_msg())
}
}
}
impl Drop for WhyNotImpl {
fn drop(&mut self) {
let ptr = self
.err
.swap(std::ptr::null_mut(), atomic::Ordering::SeqCst);
if !ptr.is_null() {
drop(
unsafe { Box::from_raw(ptr) },
);
}
}
}
impl Debug for WhyNot {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WhyNot")
.field("raw", &self.as_ref_raw())
.field("err_msg", &self.as_msg())
.finish()
}
}
#[cfg(feature = "schemars")]
impl<T> schemars::JsonSchema for ShouldBe<T>
where
T: schemars::JsonSchema,
{
fn schema_name() -> String {
T::schema_name()
}
fn json_schema(generator: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
T::json_schema(generator)
}
fn is_referenceable() -> bool {
T::is_referenceable()
}
fn schema_id() -> std::borrow::Cow<'static, str> {
T::schema_id()
}
#[doc(hidden)]
fn _schemars_private_non_optional_json_schema(
generator: &mut schemars::gen::SchemaGenerator,
) -> schemars::schema::Schema {
T::_schemars_private_non_optional_json_schema(generator)
}
#[doc(hidden)]
fn _schemars_private_is_option() -> bool {
T::_schemars_private_is_option()
}
}
pub(crate) fn is_expecting_should_be_then_reset() -> bool {
EXPECTING_SHOULD_BE.with(|cell| cell.replace(false))
}
fn take_why_not() -> Option<(Value, Error)> {
WHY_NOT.with(|cell| cell.borrow_mut().take())
}
pub(crate) fn set_why_not(raw: Value, err: Error) {
WHY_NOT.with(|cell| *cell.borrow_mut() = Some((raw, err)));
}
thread_local! {
static EXPECTING_SHOULD_BE: std::cell::RefCell<bool> = const {std::cell::RefCell::new(false)};
static WHY_NOT: std::cell::RefCell<Option<(Value, Error)>> = const {std::cell::RefCell::new(None)};
}