use core::hash::Hash;
use core::{
fmt::Debug,
marker::PhantomData,
ops::{Deref, DerefMut},
};
use iceoryx2_bb_elementary_traits::{
placement_default::PlacementDefault, zero_copy_send::ZeroCopySend,
};
use iceoryx2_log::fatal_panic;
use serde::{Deserialize, Serialize, de::Visitor};
#[repr(C, u8)]
#[derive(Default, Clone, Copy, Hash, Debug, PartialEq, Eq)]
pub enum RelocatableOption<T> {
#[default]
None,
Some(T),
}
impl<T> From<Option<T>> for RelocatableOption<T> {
fn from(value: Option<T>) -> Self {
match value {
Some(v) => RelocatableOption::Some(v),
None => RelocatableOption::None,
}
}
}
impl<T> From<RelocatableOption<T>> for Option<T> {
fn from(value: RelocatableOption<T>) -> Self {
value.to_option()
}
}
impl<T: Serialize> Serialize for RelocatableOption<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Self::Some(v) => serializer.serialize_some(v),
Self::None => serializer.serialize_none(),
}
}
}
struct RelocatableOptionVisitor<T> {
_data: PhantomData<T>,
}
impl<'de, T: Deserialize<'de>> Visitor<'de> for RelocatableOptionVisitor<T> {
type Value = RelocatableOption<T>;
fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
formatter,
"an optional value of type {}",
core::any::type_name::<T>()
)
}
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(RelocatableOption::Some(T::deserialize(deserializer)?))
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(RelocatableOption::None)
}
}
impl<'de, T: Deserialize<'de>> Deserialize<'de> for RelocatableOption<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_option(RelocatableOptionVisitor { _data: PhantomData })
}
}
unsafe impl<T: ZeroCopySend> ZeroCopySend for RelocatableOption<T> {}
impl<T: PlacementDefault> PlacementDefault for RelocatableOption<T> {
unsafe fn placement_default(ptr: *mut Self) {
unsafe { ptr.write(RelocatableOption::None) }
}
}
impl<T> RelocatableOption<T> {
pub fn to_option(self) -> Option<T> {
match self {
Self::Some(v) => Some(v),
Self::None => None,
}
}
pub fn as_option_ref(&self) -> Option<&T> {
match self {
Self::Some(v) => Some(v),
Self::None => None,
}
}
pub fn as_option_mut(&mut self) -> Option<&mut T> {
match self {
Self::Some(v) => Some(v),
Self::None => None,
}
}
pub fn as_deref(&self) -> RelocatableOption<&<T as Deref>::Target>
where
T: Deref,
{
match self {
Self::Some(v) => RelocatableOption::Some(v.deref()),
Self::None => RelocatableOption::None,
}
}
pub fn as_deref_mut(&mut self) -> RelocatableOption<&mut <T as Deref>::Target>
where
T: DerefMut,
{
match self {
Self::Some(v) => RelocatableOption::Some(v.deref_mut()),
Self::None => RelocatableOption::None,
}
}
pub fn as_mut(&mut self) -> RelocatableOption<&mut T> {
match self {
Self::Some(v) => RelocatableOption::Some(v),
Self::None => RelocatableOption::None,
}
}
pub fn as_ref(&self) -> RelocatableOption<&T> {
match self {
Self::Some(v) => RelocatableOption::Some(v),
Self::None => RelocatableOption::None,
}
}
pub fn expect(self, msg: &str) -> T {
match self {
Self::Some(v) => v,
Self::None => {
let origin = alloc::format!(
"RelocatableOption::<{}>::expect()",
core::any::type_name::<T>()
);
fatal_panic!(from origin, "Expect: {msg}");
}
}
}
pub fn inspect<F: FnOnce(&T)>(self, f: F) -> Self {
if let Self::Some(data) = &self {
f(data)
}
self
}
pub fn is_none(&self) -> bool {
match self {
RelocatableOption::None => true,
RelocatableOption::Some(_v) => false,
}
}
pub fn is_some(&self) -> bool {
!self.is_none()
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> RelocatableOption<U> {
match self {
RelocatableOption::None => RelocatableOption::None,
RelocatableOption::Some(v) => RelocatableOption::Some(f(v)),
}
}
pub fn replace(&mut self, value: T) -> RelocatableOption<T> {
core::mem::replace(self, Self::Some(value))
}
pub fn take(&mut self) -> RelocatableOption<T> {
core::mem::take(self)
}
pub fn take_if<P: FnOnce(&mut T) -> bool>(&mut self, predicate: P) -> RelocatableOption<T> {
match self {
RelocatableOption::None => RelocatableOption::None,
RelocatableOption::Some(v) => {
if predicate(v) {
core::mem::take(self)
} else {
RelocatableOption::None
}
}
}
}
pub fn unwrap(self) -> T {
match self {
Self::Some(v) => v,
Self::None => {
let origin = alloc::format!(
"RelocatableOption::<{}>::unwrap()",
core::any::type_name::<T>()
);
fatal_panic!(
from origin,
"This should never happen! Accessing the value of an empty RelocatableOption."
);
}
}
}
pub fn unwrap_or(self, alternative: T) -> T {
self.unwrap_or_else(|| alternative)
}
pub fn unwrap_or_default(self) -> T
where
T: Default,
{
self.unwrap_or_else(|| T::default())
}
pub fn unwrap_or_else<F: FnOnce() -> T>(self, f: F) -> T {
match self {
Self::Some(v) => v,
Self::None => f(),
}
}
pub unsafe fn unwrap_unchecked(self) -> T {
debug_assert!(self.is_some());
match self {
RelocatableOption::Some(v) => v,
RelocatableOption::None => unsafe { core::hint::unreachable_unchecked() },
}
}
}