use std::{marker::PhantomData, ops::Deref};
#[cfg(feature = "did-you-mean")]
use ploidy_pointer::JsonPointeeType;
use ploidy_pointer::{JsonPointee, JsonPointeeError, JsonPointer, JsonPointerTypeError};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum AbsentOr<T> {
#[default]
Absent,
Null,
Present(T),
}
impl<T> AbsentOr<T> {
#[inline]
pub fn is_absent(&self) -> bool {
matches!(self, Self::Absent)
}
#[inline]
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
#[inline]
pub fn is_present(&self) -> bool {
matches!(self, Self::Present(_))
}
#[inline]
pub fn ok(self) -> Result<T, AbsentError> {
match self {
Self::Absent => Err(AbsentError::Absent),
Self::Null => Err(AbsentError::Null),
Self::Present(value) => Ok(value),
}
}
#[inline]
pub fn as_ref(&self) -> AbsentOr<&T> {
match self {
Self::Absent => AbsentOr::Absent,
Self::Null => AbsentOr::Null,
Self::Present(value) => AbsentOr::Present(value),
}
}
#[inline]
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> AbsentOr<U> {
match self {
Self::Absent => AbsentOr::Absent,
Self::Null => AbsentOr::Null,
Self::Present(value) => AbsentOr::Present(f(value)),
}
}
#[inline]
pub fn map_or<U>(self, default: U, f: impl FnOnce(T) -> U) -> U {
match self {
Self::Absent | Self::Null => default,
Self::Present(value) => f(value),
}
}
#[inline]
pub fn map_or_else<U>(self, default: impl FnOnce() -> U, f: impl FnOnce(T) -> U) -> U {
match self {
Self::Absent | Self::Null => default(),
Self::Present(value) => f(value),
}
}
#[inline]
pub fn and<U>(self, other: AbsentOr<U>) -> AbsentOr<U> {
match self {
Self::Absent => AbsentOr::Absent,
Self::Null => AbsentOr::Null,
Self::Present(_) => other,
}
}
#[inline]
pub fn and_then<U>(self, f: impl FnOnce(T) -> AbsentOr<U>) -> AbsentOr<U> {
match self {
Self::Absent => AbsentOr::Absent,
Self::Null => AbsentOr::Null,
Self::Present(value) => f(value),
}
}
#[inline]
pub fn or(self, other: AbsentOr<T>) -> AbsentOr<T> {
match self {
Self::Present(_) => self,
Self::Absent | Self::Null => other,
}
}
#[inline]
pub fn or_else(self, f: impl FnOnce() -> AbsentOr<T>) -> AbsentOr<T> {
match self {
Self::Present(_) => self,
Self::Absent | Self::Null => f(),
}
}
#[inline]
pub fn unwrap_or(self, default: T) -> T {
match self {
Self::Absent | Self::Null => default,
Self::Present(value) => value,
}
}
#[inline]
pub fn unwrap_or_else(self, f: impl FnOnce() -> T) -> T {
match self {
Self::Absent | Self::Null => f(),
Self::Present(value) => value,
}
}
#[inline]
pub fn into_option(self) -> Option<T> {
match self {
Self::Absent | Self::Null => None,
Self::Present(value) => Some(value),
}
}
}
impl<T: Deref> AbsentOr<T> {
#[inline]
pub fn as_deref(&self) -> AbsentOr<&T::Target> {
match self {
Self::Absent => AbsentOr::Absent,
Self::Null => AbsentOr::Null,
Self::Present(value) => AbsentOr::Present(value),
}
}
}
impl<T: Default> AbsentOr<T> {
#[inline]
pub fn unwrap_or_default(self) -> T {
match self {
Self::Absent | Self::Null => T::default(),
Self::Present(value) => value,
}
}
}
impl<T> From<T> for AbsentOr<T> {
#[inline]
fn from(value: T) -> Self {
Self::Present(value)
}
}
impl<T: JsonPointee> JsonPointee for AbsentOr<T> {
fn resolve(&self, pointer: &JsonPointer) -> Result<&dyn JsonPointee, JsonPointeeError> {
match self {
Self::Present(value) => value.resolve(pointer),
_ => Err({
#[cfg(feature = "did-you-mean")]
let err = JsonPointerTypeError::with_ty(pointer, JsonPointeeType::name_of(self));
#[cfg(not(feature = "did-you-mean"))]
let err = JsonPointerTypeError::new(pointer);
err
})?,
}
}
}
impl<T: Serialize> Serialize for AbsentOr<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::Absent | Self::Null => serializer.serialize_none(),
Self::Present(value) => serializer.serialize_some(value),
}
}
}
impl<'de, T: Deserialize<'de>> Deserialize<'de> for AbsentOr<T> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct Visitor<T>(PhantomData<T>);
impl<'de, T: Deserialize<'de>> serde::de::Visitor<'de> for Visitor<T> {
type Value = AbsentOr<T>;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("`null` or value")
}
fn visit_unit<E: serde::de::Error>(self) -> Result<Self::Value, E> {
Ok(AbsentOr::Null)
}
fn visit_none<E: serde::de::Error>(self) -> Result<Self::Value, E> {
Ok(AbsentOr::Null)
}
fn visit_some<D: Deserializer<'de>>(
self,
deserializer: D,
) -> Result<Self::Value, D::Error> {
T::deserialize(deserializer).map(AbsentOr::Present)
}
}
deserializer.deserialize_option(Visitor(PhantomData))
}
}
#[derive(Debug, thiserror::Error)]
pub enum AbsentError {
#[error("value not present")]
Absent,
#[error("value is `null`")]
Null,
}
impl AbsentError {
#[inline]
pub fn field(self, name: &'static str) -> FieldAbsentError {
match self {
Self::Absent => FieldAbsentError::Absent(name),
Self::Null => FieldAbsentError::Null(name),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum FieldAbsentError {
#[error("field `{0}` not present")]
Absent(&'static str),
#[error("field `{0}` is `null`")]
Null(&'static str),
}
#[cfg(test)]
mod tests {
use ploidy_pointer::{JsonPointee, JsonPointeeExt, JsonPointerTarget};
use super::*;
#[derive(JsonPointee, JsonPointerTarget)]
#[ploidy(pointer(untagged))]
enum Response {
One(ResponseOne),
Two(ResponseTwo),
}
#[derive(JsonPointee, JsonPointerTarget)]
struct ResponseOne {
data: AbsentOr<String>,
error: AbsentOr<ResponseError>,
}
#[derive(JsonPointee, JsonPointerTarget)]
struct ResponseTwo {
data: AbsentOr<i32>,
error: AbsentOr<ResponseError>,
}
#[derive(JsonPointee, JsonPointerTarget)]
struct ResponseError {
message: String,
}
#[test]
fn test_absent_or_present_pointer_succeeds() {
let response = Response::One(ResponseOne {
error: AbsentOr::Present(ResponseError {
message: "oops".to_owned(),
}),
data: AbsentOr::Null,
});
let err = response.pointer::<&ResponseError>("/error").unwrap();
assert_eq!(err.message, "oops");
}
#[test]
fn test_absent_or_null_errors() {
let response = Response::Two(ResponseTwo {
data: AbsentOr::Present(2),
error: AbsentOr::Null,
});
let result = response.pointer::<&ResponseError>("/error");
assert!(result.is_err());
}
#[test]
fn test_absent_or_absent_errors() {
let response = Response::Two(ResponseTwo {
data: AbsentOr::Present(2),
error: AbsentOr::Absent,
});
let result = response.pointer::<&ResponseError>("/error");
assert!(result.is_err());
}
}